The Labs \ Source Viewer \ SSCLI \ System.Xml \ XmlCharCheckingWriter

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlCharCheckingWriter.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. using System;
  16. using System.IO;
  17. using System.Text;
  18. using System.Xml.XPath;
  19. using System.Xml.Schema;
  20. using System.Collections;
  21. using System.Diagnostics;
  22. namespace System.Xml
  23. {
  24.    
  25.     //
  26.     // XmlCharCheckingWriter
  27.     //
  28.     internal class XmlCharCheckingWriter : XmlWrappingWriter
  29.     {
  30.         //
  31.         // Fields
  32.         //
  33.         bool checkValues;
  34.         bool checkNames;
  35.         bool replaceNewLines;
  36.         string newLineChars;
  37.        
  38.         XmlCharType xmlCharType;
  39.         //
  40.         // Constructor
  41.         //
  42.         internal XmlCharCheckingWriter(XmlWriter baseWriter, bool checkValues, bool checkNames, bool replaceNewLines, string newLineChars) : base(baseWriter)
  43.         {
  44.            
  45.             Debug.Assert(checkValues || replaceNewLines);
  46.             this.checkValues = checkValues;
  47.             this.checkNames = checkNames;
  48.             this.replaceNewLines = replaceNewLines;
  49.             this.newLineChars = newLineChars;
  50.            
  51.             if (checkValues) {
  52.                 xmlCharType = XmlCharType.Instance;
  53.             }
  54.         }
  55.        
  56.         //
  57.         // XmlWriter implementation
  58.         //
  59.        
  60.         public override XmlWriterSettings Settings {
  61.             get {
  62.                 XmlWriterSettings s = base.writer.Settings;
  63.                 if (s == null) {
  64.                     s = new XmlWriterSettings();
  65.                 }
  66.                 else {
  67.                     s = s.Clone();
  68.                 }
  69.                 if (checkValues) {
  70.                     s.CheckCharacters = true;
  71.                 }
  72.                 if (replaceNewLines) {
  73.                     s.NewLineHandling = NewLineHandling.Replace;
  74.                     s.NewLineChars = newLineChars;
  75.                 }
  76.                 s.ReadOnly = true;
  77.                 return s;
  78.             }
  79.         }
  80.        
  81.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  82.         {
  83.             if (checkNames) {
  84.                 ValidateQName(name);
  85.             }
  86.             if (checkValues) {
  87.                 if (pubid != null) {
  88.                     int i;
  89.                     if ((i = xmlCharType.IsPublicId(pubid)) >= 0) {
  90.                         throw XmlConvert.CreateInvalidCharException(pubid[i]);
  91.                     }
  92.                 }
  93.                 if (sysid != null) {
  94.                     CheckCharacters(sysid);
  95.                 }
  96.                 if (subset != null) {
  97.                     CheckCharacters(subset);
  98.                 }
  99.             }
  100.             if (replaceNewLines) {
  101.                 sysid = ReplaceNewLines(sysid);
  102.                 pubid = ReplaceNewLines(pubid);
  103.                 subset = ReplaceNewLines(subset);
  104.             }
  105.             writer.WriteDocType(name, pubid, sysid, subset);
  106.         }
  107.        
  108.         public override void WriteStartElement(string prefix, string localName, string ns)
  109.         {
  110.             if (checkNames) {
  111.                 if (localName == null || localName.Length == 0) {
  112.                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
  113.                 }
  114.                 ValidateNCName(localName);
  115.                
  116.                 if (prefix != null && prefix.Length > 0) {
  117.                     ValidateNCName(prefix);
  118.                 }
  119.             }
  120.             writer.WriteStartElement(prefix, localName, ns);
  121.         }
  122.        
  123.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  124.         {
  125.             if (checkNames) {
  126.                 if (localName == null || localName.Length == 0) {
  127.                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
  128.                 }
  129.                 ValidateNCName(localName);
  130.                
  131.                 if (prefix != null && prefix.Length > 0) {
  132.                     ValidateNCName(prefix);
  133.                 }
  134.             }
  135.             writer.WriteStartAttribute(prefix, localName, ns);
  136.         }
  137.        
  138.         public override void WriteCData(string text)
  139.         {
  140.             if (text != null) {
  141.                 if (checkValues) {
  142.                     CheckCharacters(text);
  143.                 }
  144.                 if (replaceNewLines) {
  145.                     text = ReplaceNewLines(text);
  146.                 }
  147.                 int i;
  148.                 while ((i = text.IndexOf("]]>", StringComparison.Ordinal)) >= 0) {
  149.                     writer.WriteCData(text.Substring(0, i + 2));
  150.                     text = text.Substring(i + 2);
  151.                 }
  152.             }
  153.             writer.WriteCData(text);
  154.         }
  155.        
  156.         public override void WriteComment(string text)
  157.         {
  158.             if (text != null) {
  159.                 if (checkValues) {
  160.                     CheckCharacters(text);
  161.                     text = InterleaveInvalidChars(text, '-', '-');
  162.                 }
  163.                 if (replaceNewLines) {
  164.                     text = ReplaceNewLines(text);
  165.                 }
  166.             }
  167.             writer.WriteComment(text);
  168.         }
  169.        
  170.         public override void WriteProcessingInstruction(string name, string text)
  171.         {
  172.             if (checkNames) {
  173.                 ValidateNCName(name);
  174.             }
  175.             if (text != null) {
  176.                 if (checkValues) {
  177.                     CheckCharacters(text);
  178.                     text = InterleaveInvalidChars(text, '?', '>');
  179.                 }
  180.                 if (replaceNewLines) {
  181.                     text = ReplaceNewLines(text);
  182.                 }
  183.             }
  184.             writer.WriteProcessingInstruction(name, text);
  185.         }
  186.        
  187.         public override void WriteEntityRef(string name)
  188.         {
  189.             if (checkNames) {
  190.                 ValidateQName(name);
  191.             }
  192.             writer.WriteEntityRef(name);
  193.         }
  194.        
  195.         public override void WriteWhitespace(string ws)
  196.         {
  197.             // "checkNames" is intentional here; if false, the whitespaces are checked in XmlWellformedWriter
  198.             if (checkNames) {
  199.                 int i;
  200.                 if ((i = xmlCharType.IsOnlyWhitespaceWithPos(ws)) != -1) {
  201.                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidWhitespaceCharacter, XmlException.BuildCharExceptionStr(ws[i])));
  202.                 }
  203.             }
  204.             writer.WriteWhitespace(ws);
  205.         }
  206.        
  207.         public override void WriteString(string text)
  208.         {
  209.             if (text != null) {
  210.                 if (checkValues) {
  211.                     CheckCharacters(text);
  212.                 }
  213.                 if (replaceNewLines && WriteState != WriteState.Attribute) {
  214.                     text = ReplaceNewLines(text);
  215.                 }
  216.             }
  217.             writer.WriteString(text);
  218.         }
  219.        
  220.         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
  221.         {
  222.             writer.WriteSurrogateCharEntity(lowChar, highChar);
  223.         }
  224.        
  225.         public override void WriteChars(char[] buffer, int index, int count)
  226.         {
  227.             if (checkValues) {
  228.                 CheckCharacters(buffer, index, count);
  229.             }
  230.             if (replaceNewLines && WriteState != WriteState.Attribute) {
  231.                 string text = ReplaceNewLines(buffer, index, count);
  232.                 if (text != null) {
  233.                     WriteString(text);
  234.                     return;
  235.                 }
  236.             }
  237.             writer.WriteChars(buffer, index, count);
  238.         }
  239.        
  240.         public override void WriteNmToken(string name)
  241.         {
  242.             if (checkNames) {
  243.                 if (name == null || name.Length == 0) {
  244.                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  245.                 }
  246.                 XmlConvert.VerifyNMTOKEN(name);
  247.             }
  248.             writer.WriteNmToken(name);
  249.         }
  250.        
  251.         public override void WriteName(string name)
  252.         {
  253.             if (checkNames) {
  254.                 XmlConvert.VerifyQName(name);
  255.             }
  256.             writer.WriteName(name);
  257.         }
  258.        
  259.         public override void WriteQualifiedName(string localName, string ns)
  260.         {
  261.             if (checkNames) {
  262.                 ValidateNCName(localName);
  263.             }
  264.             writer.WriteQualifiedName(localName, ns);
  265.         }
  266.        
  267.         //
  268.         // Private methods
  269.         //
  270.         private void CheckCharacters(string str)
  271.         {
  272.             XmlConvert.VerifyCharData(str, ExceptionType.ArgumentException);
  273.         }
  274.        
  275.         private void CheckCharacters(char[] data, int offset, int len)
  276.         {
  277.             XmlConvert.VerifyCharData(data, offset, len, ExceptionType.ArgumentException);
  278.         }
  279.        
  280.         private void ValidateNCName(string ncname)
  281.         {
  282.             if (ncname.Length == 0) {
  283.                 throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  284.             }
  285.             int len = ValidateNames.ParseNCName(ncname, 0);
  286.             if (len != ncname.Length) {
  287.                 throw new ArgumentException(Res.GetString(len == 0 ? Res.Xml_BadStartNameChar : Res.Xml_BadNameChar, XmlException.BuildCharExceptionStr(ncname[len])));
  288.             }
  289.         }
  290.        
  291.         private void ValidateQName(string name)
  292.         {
  293.             if (name.Length == 0) {
  294.                 throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  295.             }
  296.             int colonPos;
  297.             int len = ValidateNames.ParseQName(name, 0, out colonPos);
  298.             if (len != name.Length) {
  299.                 string res = (len == 0 || (colonPos > -1 && len == colonPos + 1)) ? Res.Xml_BadStartNameChar : Res.Xml_BadNameChar;
  300.                 throw new ArgumentException(Res.GetString(res, XmlException.BuildCharExceptionStr(name[len])));
  301.             }
  302.         }
  303.        
  304.         private string ReplaceNewLines(string str)
  305.         {
  306.             if (str == null) {
  307.                 return null;
  308.             }
  309.            
  310.             StringBuilder sb = null;
  311.             int start = 0;
  312.             int i;
  313.             for (i = 0; i < str.Length; i++) {
  314.                 char ch;
  315.                 if ((ch = str[i]) >= 32) {
  316.                     continue;
  317.                 }
  318.                 if (ch == '\n') {
  319.                     if (newLineChars == "\n") {
  320.                         continue;
  321.                     }
  322.                     if (sb == null) {
  323.                         sb = new StringBuilder(str.Length + 5);
  324.                     }
  325.                     sb.Append(str, start, i - start);
  326.                 }
  327.                 else if (ch == '\r') {
  328.                     if (i + 1 < str.Length && str[i + 1] == '\n') {
  329.                         if (newLineChars == "\r\n") {
  330.                             i++;
  331.                             continue;
  332.                         }
  333.                         if (sb == null) {
  334.                             sb = new StringBuilder(str.Length + 5);
  335.                         }
  336.                         sb.Append(str, start, i - start);
  337.                         i++;
  338.                     }
  339.                     else {
  340.                         if (newLineChars == "\r") {
  341.                             continue;
  342.                         }
  343.                         if (sb == null) {
  344.                             sb = new StringBuilder(str.Length + 5);
  345.                         }
  346.                         sb.Append(str, start, i - start);
  347.                     }
  348.                 }
  349.                 else {
  350.                     continue;
  351.                 }
  352.                 sb.Append(newLineChars);
  353.                 start = i + 1;
  354.             }
  355.            
  356.             if (sb == null) {
  357.                 return str;
  358.             }
  359.             else {
  360.                 sb.Append(str, start, i - start);
  361.                 return sb.ToString();
  362.             }
  363.         }
  364.        
  365.         private string ReplaceNewLines(char[] data, int offset, int len)
  366.         {
  367.             if (data == null) {
  368.                 return null;
  369.             }
  370.            
  371.             StringBuilder sb = null;
  372.             int start = offset;
  373.             int endPos = offset + len;
  374.             int i;
  375.             for (i = offset; i < endPos; i++) {
  376.                 char ch;
  377.                 if ((ch = data[i]) >= 32) {
  378.                     continue;
  379.                 }
  380.                 if (ch == '\n') {
  381.                     if (newLineChars == "\n") {
  382.                         continue;
  383.                     }
  384.                     if (sb == null) {
  385.                         sb = new StringBuilder(len + 5);
  386.                     }
  387.                     sb.Append(data, start, i - start);
  388.                 }
  389.                 else if (ch == '\r') {
  390.                     if (i + 1 < endPos && data[i + 1] == '\n') {
  391.                         if (newLineChars == "\r\n") {
  392.                             continue;
  393.                         }
  394.                         if (sb == null) {
  395.                             sb = new StringBuilder(len + 5);
  396.                         }
  397.                         sb.Append(data, start, i - start);
  398.                         i++;
  399.                     }
  400.                     else {
  401.                         if (newLineChars == "\r") {
  402.                             continue;
  403.                         }
  404.                         if (sb == null) {
  405.                             sb = new StringBuilder(len + 5);
  406.                         }
  407.                         sb.Append(data, start, i - start);
  408.                     }
  409.                 }
  410.                 else {
  411.                     continue;
  412.                 }
  413.                 sb.Append(newLineChars);
  414.                 start = i + 1;
  415.             }
  416.            
  417.             if (sb == null) {
  418.                 return null;
  419.             }
  420.             else {
  421.                 sb.Append(data, start, i - start);
  422.                 return sb.ToString();
  423.             }
  424.         }
  425.        
  426.         // interleave 2 adjacent invalid chars with a space, e.g. change "--" to "- -"
  427.         private string InterleaveInvalidChars(string text, char invChar1, char invChar2)
  428.         {
  429.             StringBuilder sb = null;
  430.             int start = 0;
  431.             int i;
  432.             for (i = 0; i < text.Length; i++) {
  433.                 if (text[i] != invChar2) {
  434.                     continue;
  435.                 }
  436.                 if (i > 0 && text[i - 1] == invChar1) {
  437.                     if (sb == null) {
  438.                         sb = new StringBuilder(text.Length + 5);
  439.                     }
  440.                     sb.Append(text, start, i - start);
  441.                     sb.Append(' ');
  442.                     start = i;
  443.                 }
  444.             }
  445.            
  446.             // check last char & return
  447.             if (sb == null) {
  448.                 return (i == 0 || text[i - 1] != invChar1) ? text : (text + ' ');
  449.             }
  450.             else {
  451.                 sb.Append(text, start, i - start);
  452.                 if (i > 0 && text[i - 1] == invChar1) {
  453.                     sb.Append(' ');
  454.                 }
  455.                 return sb.ToString();
  456.             }
  457.         }
  458.     }
  459. }

Developer Fusion