XmlUtilWriter
using System.Globalization;
using System.IO;
using System.Xml;
namespace System.Configuration
{
internal sealed class XmlUtilWriter
{
private sealed class StreamWriterCheckpoint
{
internal readonly bool _isLastLineBlank;
internal readonly int _lineNumber;
internal readonly int _linePosition;
internal readonly long _streamLength;
internal readonly long _streamPosition;
internal StreamWriterCheckpoint(XmlUtilWriter writer)
{
writer.Flush();
_lineNumber = writer.LineNumber;
_linePosition = writer.LinePosition;
_isLastLineBlank = writer.IsLastLineBlank;
writer._baseStream.Flush();
_streamPosition = writer._baseStream.Position;
_streamLength = writer._baseStream.Length;
}
}
private const char Space = ' ';
private const string NewLine = "\r\n";
private static readonly string s_spaces8 = new string(' ', 8);
private static readonly string s_spaces4 = new string(' ', 4);
private static readonly string s_spaces2 = new string(' ', 2);
private readonly Stream _baseStream;
private object _lineStartCheckpoint;
internal TextWriter Writer { get; }
internal bool TrackPosition { get; }
internal int LineNumber { get; set; }
internal int LinePosition { get; set; }
internal bool IsLastLineBlank { get; set; }
internal XmlUtilWriter(TextWriter writer, bool trackPosition)
{
Writer = writer;
TrackPosition = trackPosition;
LineNumber = 1;
LinePosition = 1;
IsLastLineBlank = true;
if (TrackPosition) {
_baseStream = ((StreamWriter)Writer).BaseStream;
_lineStartCheckpoint = CreateStreamCheckpoint();
}
}
private void UpdatePosition(char ch)
{
switch (ch) {
case '\r':
LineNumber++;
LinePosition = 1;
IsLastLineBlank = true;
break;
case '\n':
_lineStartCheckpoint = CreateStreamCheckpoint();
break;
case '\t':
case ' ':
LinePosition++;
break;
default:
LinePosition++;
IsLastLineBlank = false;
break;
}
}
internal int Write(string s)
{
if (TrackPosition) {
foreach (char c in s) {
Writer.Write(c);
UpdatePosition(c);
}
} else
Writer.Write(s);
return s.Length;
}
internal int Write(char ch)
{
Writer.Write(ch);
if (TrackPosition)
UpdatePosition(ch);
return 1;
}
internal void Flush()
{
Writer.Flush();
}
internal int AppendEscapeTextString(string s)
{
return AppendEscapeXmlString(s, false, 'A');
}
internal int AppendEscapeXmlString(string s, bool inAttribute, char quoteChar)
{
int num = 0;
foreach (char c in s) {
bool flag = false;
string text = null;
if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') || c > '�')
flag = true;
else {
switch (c) {
case '<':
text = "lt";
break;
case '>':
text = "gt";
break;
case '&':
text = "amp";
break;
case '\'':
if (inAttribute && quoteChar == c)
text = "apos";
break;
case '"':
if (inAttribute && quoteChar == c)
text = "quot";
break;
case '\n':
case '\r':
flag = inAttribute;
break;
}
}
num = ((!flag) ? ((text == null) ? (num + Write(c)) : (num + AppendEntityRef(text))) : (num + AppendCharEntity(c)));
}
return num;
}
internal int AppendEntityRef(string entityRef)
{
Write('&');
Write(entityRef);
Write(';');
return entityRef.Length + 2;
}
internal int AppendCharEntity(char ch)
{
int num = ch;
string text = num.ToString("X", CultureInfo.InvariantCulture);
Write('&');
Write('#');
Write('x');
Write(text);
Write(';');
return text.Length + 4;
}
internal int AppendCData(string cdata)
{
Write("<![CDATA[");
Write(cdata);
Write("]]>");
return cdata.Length + 12;
}
internal int AppendProcessingInstruction(string name, string value)
{
Write("<?");
Write(name);
AppendSpace();
Write(value);
Write("?>");
return name.Length + value.Length + 5;
}
internal int AppendComment(string comment)
{
Write("<!--");
Write(comment);
Write("-->");
return comment.Length + 7;
}
internal int AppendAttributeValue(XmlTextReader reader)
{
int num = 0;
char c = reader.QuoteChar;
if (c != '"' && c != '\'')
c = '"';
num += Write(c);
while (reader.ReadAttributeValue()) {
num = ((reader.NodeType != XmlNodeType.Text) ? (num + AppendEntityRef(reader.Name)) : (num + AppendEscapeXmlString(reader.Value, true, c)));
}
return num + Write(c);
}
internal int AppendRequiredWhiteSpace(int fromLineNumber, int fromLinePosition, int toLineNumber, int toLinePosition)
{
int num = AppendWhiteSpace(fromLineNumber, fromLinePosition, toLineNumber, toLinePosition);
if (num == 0)
num += AppendSpace();
return num;
}
internal int AppendWhiteSpace(int fromLineNumber, int fromLinePosition, int toLineNumber, int toLinePosition)
{
int num = 0;
while (fromLineNumber++ < toLineNumber) {
num += AppendNewLine();
fromLinePosition = 1;
}
return num + AppendSpaces(toLinePosition - fromLinePosition);
}
internal int AppendIndent(int linePosition, int indent, int depth, bool newLine)
{
int num = 0;
if (newLine)
num += AppendNewLine();
int count = linePosition - 1 + indent * depth;
return num + AppendSpaces(count);
}
internal int AppendSpacesToLinePosition(int linePosition)
{
if (linePosition <= 0)
return 0;
int num = linePosition - LinePosition;
if (num < 0 && IsLastLineBlank)
SeekToLineStart();
return AppendSpaces(linePosition - LinePosition);
}
internal int AppendNewLine()
{
return Write("\r\n");
}
internal int AppendSpaces(int count)
{
int num = count;
while (num > 0) {
if (num >= 8) {
Write(s_spaces8);
num -= 8;
} else if (num >= 4) {
Write(s_spaces4);
num -= 4;
} else {
if (num < 2) {
Write(' ');
break;
}
Write(s_spaces2);
num -= 2;
}
}
if (count <= 0)
return 0;
return count;
}
internal int AppendSpace()
{
return Write(' ');
}
internal void SeekToLineStart()
{
RestoreStreamCheckpoint(_lineStartCheckpoint);
}
internal object CreateStreamCheckpoint()
{
return new StreamWriterCheckpoint(this);
}
internal void RestoreStreamCheckpoint(object o)
{
StreamWriterCheckpoint streamWriterCheckpoint = (StreamWriterCheckpoint)o;
Flush();
LineNumber = streamWriterCheckpoint._lineNumber;
LinePosition = streamWriterCheckpoint._linePosition;
IsLastLineBlank = streamWriterCheckpoint._isLastLineBlank;
_baseStream.Seek(streamWriterCheckpoint._streamPosition, SeekOrigin.Begin);
_baseStream.SetLength(streamWriterCheckpoint._streamLength);
_baseStream.Flush();
}
}
}