|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Text;
using Microsoft.Xml.Schema;
using System.Collections;
using System.Diagnostics;
namespace Microsoft.Xml
{
using System;
//
// XmlCharCheckingWriter
//
internal partial class XmlCharCheckingWriter : XmlWrappingWriter
{
//
// Fields
//
private bool _checkValues;
private bool _checkNames;
private bool _replaceNewLines;
private string _newLineChars;
private XmlCharType _xmlCharType;
//
// Constructor
//
internal XmlCharCheckingWriter(XmlWriter baseWriter, bool checkValues, bool checkNames, bool replaceNewLines, string newLineChars)
: base(baseWriter)
{
Debug.Assert(checkValues || replaceNewLines);
_checkValues = checkValues;
_checkNames = checkNames;
_replaceNewLines = replaceNewLines;
_newLineChars = newLineChars;
if (checkValues)
{
_xmlCharType = XmlCharType.Instance;
}
}
//
// XmlWriter implementation
//
public override XmlWriterSettings Settings
{
get
{
XmlWriterSettings s = base.writer.Settings;
s = (s != null) ? (XmlWriterSettings)s.Clone() : new XmlWriterSettings();
if (_checkValues)
{
s.CheckCharacters = true;
}
if (_replaceNewLines)
{
s.NewLineHandling = NewLineHandling.Replace;
s.NewLineChars = _newLineChars;
}
s.ReadOnly = true;
return s;
}
}
public override void WriteDocType(string name, string pubid, string sysid, string subset)
{
if (_checkNames)
{
ValidateQName(name);
}
if (_checkValues)
{
if (pubid != null)
{
int i;
if ((i = _xmlCharType.IsPublicId(pubid)) >= 0)
{
throw XmlConvert.CreateInvalidCharException(pubid, i);
}
}
if (sysid != null)
{
CheckCharacters(sysid);
}
if (subset != null)
{
CheckCharacters(subset);
}
}
if (_replaceNewLines)
{
sysid = ReplaceNewLines(sysid);
pubid = ReplaceNewLines(pubid);
subset = ReplaceNewLines(subset);
}
writer.WriteDocType(name, pubid, sysid, subset);
}
public override void WriteStartElement(string prefix, string localName, string ns)
{
if (_checkNames)
{
if (localName == null || localName.Length == 0)
{
throw new ArgumentException(ResXml.Xml_EmptyLocalName);
}
ValidateNCName(localName);
if (prefix != null && prefix.Length > 0)
{
ValidateNCName(prefix);
}
}
writer.WriteStartElement(prefix, localName, ns);
}
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
if (_checkNames)
{
if (localName == null || localName.Length == 0)
{
throw new ArgumentException(ResXml.Xml_EmptyLocalName);
}
ValidateNCName(localName);
if (prefix != null && prefix.Length > 0)
{
ValidateNCName(prefix);
}
}
writer.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteCData(string text)
{
if (text != null)
{
if (_checkValues)
{
CheckCharacters(text);
}
if (_replaceNewLines)
{
text = ReplaceNewLines(text);
}
int i;
while ((i = text.IndexOf("]]>", StringComparison.Ordinal)) >= 0)
{
writer.WriteCData(text.Substring(0, i + 2));
text = text.Substring(i + 2);
}
}
writer.WriteCData(text);
}
public override void WriteComment(string text)
{
if (text != null)
{
if (_checkValues)
{
CheckCharacters(text);
text = InterleaveInvalidChars(text, '-', '-');
}
if (_replaceNewLines)
{
text = ReplaceNewLines(text);
}
}
writer.WriteComment(text);
}
public override void WriteProcessingInstruction(string name, string text)
{
if (_checkNames)
{
ValidateNCName(name);
}
if (text != null)
{
if (_checkValues)
{
CheckCharacters(text);
text = InterleaveInvalidChars(text, '?', '>');
}
if (_replaceNewLines)
{
text = ReplaceNewLines(text);
}
}
writer.WriteProcessingInstruction(name, text);
}
public override void WriteEntityRef(string name)
{
if (_checkNames)
{
ValidateQName(name);
}
writer.WriteEntityRef(name);
}
public override void WriteWhitespace(string ws)
{
if (ws == null)
{
ws = string.Empty;
}
// "checkNames" is intentional here; if false, the whitespaces are checked in XmlWellformedWriter
if (_checkNames)
{
int i;
if ((i = _xmlCharType.IsOnlyWhitespaceWithPos(ws)) != -1)
{
throw new ArgumentException(string.Format(ResXml.Xml_InvalidWhitespaceCharacter, XmlException.BuildCharExceptionArgs(ws, i)));
}
}
if (_replaceNewLines)
{
ws = ReplaceNewLines(ws);
}
writer.WriteWhitespace(ws);
}
public override void WriteString(string text)
{
if (text != null)
{
if (_checkValues)
{
CheckCharacters(text);
}
if (_replaceNewLines && WriteState != WriteState.Attribute)
{
text = ReplaceNewLines(text);
}
}
writer.WriteString(text);
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
writer.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteChars(char[] buffer, int index, int count)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (index < 0)
{
throw new ArgumentOutOfRangeException("index");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (count > buffer.Length - index)
{
throw new ArgumentOutOfRangeException("count");
}
if (_checkValues)
{
CheckCharacters(buffer, index, count);
}
if (_replaceNewLines && WriteState != WriteState.Attribute)
{
string text = ReplaceNewLines(buffer, index, count);
if (text != null)
{
WriteString(text);
return;
}
}
writer.WriteChars(buffer, index, count);
}
public override void WriteNmToken(string name)
{
if (_checkNames)
{
if (name == null || name.Length == 0)
{
throw new ArgumentException(ResXml.Xml_EmptyName);
}
XmlConvert.VerifyNMTOKEN(name);
}
writer.WriteNmToken(name);
}
public override void WriteName(string name)
{
if (_checkNames)
{
XmlConvert.VerifyQName(name, ExceptionType.XmlException);
}
writer.WriteName(name);
}
public override void WriteQualifiedName(string localName, string ns)
{
if (_checkNames)
{
ValidateNCName(localName);
}
writer.WriteQualifiedName(localName, ns);
}
//
// Private methods
//
private void CheckCharacters(string str)
{
XmlConvert.VerifyCharData(str, ExceptionType.ArgumentException);
}
private void CheckCharacters(char[] data, int offset, int len)
{
XmlConvert.VerifyCharData(data, offset, len, ExceptionType.ArgumentException);
}
private void ValidateNCName(string ncname)
{
if (ncname.Length == 0)
{
throw new ArgumentException(ResXml.Xml_EmptyName);
}
int len = ValidateNames.ParseNCName(ncname, 0);
if (len != ncname.Length)
{
throw new ArgumentException(string.Format(len == 0 ? ResXml.Xml_BadStartNameChar : ResXml.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(ncname, len)));
}
}
private void ValidateQName(string name)
{
if (name.Length == 0)
{
throw new ArgumentException(ResXml.Xml_EmptyName);
}
int colonPos;
int len = ValidateNames.ParseQName(name, 0, out colonPos);
if (len != name.Length)
{
string res = (len == 0 || (colonPos > -1 && len == colonPos + 1)) ? ResXml.Xml_BadStartNameChar : ResXml.Xml_BadNameChar;
throw new ArgumentException(string.Format(res, XmlException.BuildCharExceptionArgs(name, len)));
}
}
private string ReplaceNewLines(string str)
{
if (str == null)
{
return null;
}
StringBuilder sb = null;
int start = 0;
int i;
for (i = 0; i < str.Length; i++)
{
char ch;
if ((ch = str[i]) >= 0x20)
{
continue;
}
if (ch == '\n')
{
if (_newLineChars == "\n")
{
continue;
}
if (sb == null)
{
sb = new StringBuilder(str.Length + 5);
}
sb.Append(str, start, i - start);
}
else if (ch == '\r')
{
if (i + 1 < str.Length && str[i + 1] == '\n')
{
if (_newLineChars == "\r\n")
{
i++;
continue;
}
if (sb == null)
{
sb = new StringBuilder(str.Length + 5);
}
sb.Append(str, start, i - start);
i++;
}
else
{
if (_newLineChars == "\r")
{
continue;
}
if (sb == null)
{
sb = new StringBuilder(str.Length + 5);
}
sb.Append(str, start, i - start);
}
}
else
{
continue;
}
sb.Append(_newLineChars);
start = i + 1;
}
if (sb == null)
{
return str;
}
else
{
sb.Append(str, start, i - start);
return sb.ToString();
}
}
private string ReplaceNewLines(char[] data, int offset, int len)
{
if (data == null)
{
return null;
}
StringBuilder sb = null;
int start = offset;
int endPos = offset + len;
int i;
for (i = offset; i < endPos; i++)
{
char ch;
if ((ch = data[i]) >= 0x20)
{
continue;
}
if (ch == '\n')
{
if (_newLineChars == "\n")
{
continue;
}
if (sb == null)
{
sb = new StringBuilder(len + 5);
}
sb.Append(data, start, i - start);
}
else if (ch == '\r')
{
if (i + 1 < endPos && data[i + 1] == '\n')
{
if (_newLineChars == "\r\n")
{
i++;
continue;
}
if (sb == null)
{
sb = new StringBuilder(len + 5);
}
sb.Append(data, start, i - start);
i++;
}
else
{
if (_newLineChars == "\r")
{
continue;
}
if (sb == null)
{
sb = new StringBuilder(len + 5);
}
sb.Append(data, start, i - start);
}
}
else
{
continue;
}
sb.Append(_newLineChars);
start = i + 1;
}
if (sb == null)
{
return null;
}
else
{
sb.Append(data, start, i - start);
return sb.ToString();
}
}
// Interleave 2 adjacent invalid chars with a space. This is used for fixing invalid values of comments and PIs.
// Any "--" in comment must be replaced with "- -" and any "-" at the end must be appended with " ".
// Any "?>" in PI value must be replaced with "? >".
// This code has a bug SQL BU Defect Tracking #480848, which was triaged as Won't Fix because it is a breaking change
private string InterleaveInvalidChars(string text, char invChar1, char invChar2)
{
StringBuilder sb = null;
int start = 0;
int i;
for (i = 0; i < text.Length; i++)
{
if (text[i] != invChar2)
{
continue;
}
if (i > 0 && text[i - 1] == invChar1)
{
if (sb == null)
{
sb = new StringBuilder(text.Length + 5);
}
sb.Append(text, start, i - start);
sb.Append(' ');
start = i;
}
}
// check last char & return
if (sb == null)
{
return (i == 0 || text[i - 1] != invChar1) ? text : (text + ' ');
}
else
{
sb.Append(text, start, i - start);
if (i > 0 && text[i - 1] == invChar1)
{
sb.Append(' ');
}
return sb.ToString();
}
}
}
}
|