|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.IdentityModel
{
using System.IO;
using System.Xml;
using System.Text;
using System.Diagnostics;
using HexBinary = System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary;
sealed class WrappedReader : DelegatingXmlDictionaryReader, IXmlLineInfo
{
XmlTokenStream xmlTokens;
MemoryStream contentStream;
TextReader contentReader;
bool recordDone;
int depth;
bool disposed;
public WrappedReader(XmlDictionaryReader reader)
{
if (reader == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
}
if (!reader.IsStartElement())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.InnerReaderMustBeAtElement)));
}
this.xmlTokens = new XmlTokenStream(32);
base.InitializeInnerReader(reader);
Record();
}
public int LineNumber
{
get
{
IXmlLineInfo lineInfo = base.InnerReader as IXmlLineInfo;
if (lineInfo == null)
{
return 1;
}
return lineInfo.LineNumber;
}
}
public int LinePosition
{
get
{
IXmlLineInfo lineInfo = base.InnerReader as IXmlLineInfo;
if (lineInfo == null)
{
return 1;
}
return lineInfo.LinePosition;
}
}
public XmlTokenStream XmlTokens
{
get { return this.xmlTokens; }
}
public override void Close()
{
OnEndOfContent();
base.InnerReader.Close();
}
public bool HasLineInfo()
{
IXmlLineInfo lineInfo = base.InnerReader as IXmlLineInfo;
return lineInfo != null && lineInfo.HasLineInfo();
}
public override void MoveToAttribute(int index)
{
OnEndOfContent();
base.InnerReader.MoveToAttribute(index);
}
public override bool MoveToAttribute(string name)
{
OnEndOfContent();
return base.InnerReader.MoveToAttribute(name);
}
public override bool MoveToAttribute(string name, string ns)
{
OnEndOfContent();
return base.InnerReader.MoveToAttribute(name, ns);
}
public override bool MoveToElement()
{
OnEndOfContent();
return base.MoveToElement();
}
public override bool MoveToFirstAttribute()
{
OnEndOfContent();
return base.MoveToFirstAttribute();
}
public override bool MoveToNextAttribute()
{
OnEndOfContent();
return base.MoveToNextAttribute();
}
void OnEndOfContent()
{
if (this.contentReader != null)
{
this.contentReader.Close();
this.contentReader = null;
}
if (this.contentStream != null)
{
this.contentStream.Close();
this.contentStream = null;
}
}
public override bool Read()
{
OnEndOfContent();
if (!base.Read())
{
return false;
}
if (!this.recordDone)
{
Record();
}
return true;
}
int ReadBinaryContent(byte[] buffer, int offset, int count, bool isBase64)
{
CryptoHelper.ValidateBufferBounds(buffer, offset, count);
//
// Concatentate text nodes to get entire element value before attempting to convert
// XmlDictionaryReader.CreateDictionaryReader( XmlReader ) creates a reader that returns base64 in a single text node
// XmlDictionaryReader.CreateTextReader( Stream ) creates a reader that produces multiple text and whitespace nodes
// Attribute nodes consist of only a single value
//
if (this.contentStream == null)
{
string encodedValue;
if (NodeType == XmlNodeType.Attribute)
{
encodedValue = Value;
}
else
{
StringBuilder fullText = new StringBuilder(1000);
while (NodeType != XmlNodeType.Element && NodeType != XmlNodeType.EndElement)
{
switch (NodeType)
{
// concatenate text nodes
case XmlNodeType.Text:
fullText.Append(Value);
break;
// skip whitespace
case XmlNodeType.Whitespace:
break;
}
Read();
}
encodedValue = fullText.ToString();
}
byte[] value = isBase64 ? Convert.FromBase64String(encodedValue) : HexBinary.Parse(encodedValue).Value;
this.contentStream = new MemoryStream(value);
}
int read = this.contentStream.Read(buffer, offset, count);
if (read == 0)
{
this.contentStream.Close();
this.contentStream = null;
}
return read;
}
// CodeReviewQ: The commented method was original System.IdentityModel method to ReadBinaryContent. The one in Microsoft,IdentityModel is coded differently. See above.
// I have done primitive conceptual level validation and have kept the M.IM's method and have commented the S.IM's method. But we need prescriptive guidance
// here as we may be potentailly breaking WCF customers with this change. To avoid breaking WCF's existing customers we need to uncommented the method below and comment the one above.
//int ReadBinaryContent(byte[] buffer, int offset, int count, bool isBase64)
//{
// CryptoHelper.ValidateBufferBounds(buffer, offset, count);
// int read = 0;
// while (count > 0 && this.NodeType != XmlNodeType.Element && this.NodeType != XmlNodeType.EndElement)
// {
// if (this.contentStream == null)
// {
// byte[] value = isBase64 ? Convert.FromBase64String(this.Value) : HexBinary.Parse(this.Value).Value;
// this.contentStream = new MemoryStream(value);
// }
// int actual = this.contentStream.Read(buffer, offset, count);
// if (actual == 0)
// {
// if (this.NodeType == XmlNodeType.Attribute)
// {
// break;
// }
// if (!Read())
// {
// break;
// }
// }
// read += actual;
// offset += actual;
// count -= actual;
// }
// return read;
//}
public override int ReadContentAsBase64(byte[] buffer, int offset, int count)
{
return ReadBinaryContent(buffer, offset, count, true);
}
public override int ReadContentAsBinHex(byte[] buffer, int offset, int count)
{
return ReadBinaryContent(buffer, offset, count, false);
}
public override int ReadValueChunk(char[] chars, int offset, int count)
{
if (this.contentReader == null)
{
this.contentReader = new StringReader(Value);
}
return this.contentReader.Read(chars, offset, count);
}
void Record()
{
switch (NodeType)
{
case XmlNodeType.Element:
{
bool isEmpty = base.InnerReader.IsEmptyElement;
this.xmlTokens.AddElement(base.InnerReader.Prefix, base.InnerReader.LocalName, base.InnerReader.NamespaceURI, isEmpty);
if (base.InnerReader.MoveToFirstAttribute())
{
do
{
this.xmlTokens.AddAttribute(base.InnerReader.Prefix, base.InnerReader.LocalName, base.InnerReader.NamespaceURI, base.InnerReader.Value);
}
while (base.InnerReader.MoveToNextAttribute());
base.InnerReader.MoveToElement();
}
if (!isEmpty)
{
this.depth++;
}
else if (this.depth == 0)
{
this.recordDone = true;
}
break;
}
case XmlNodeType.CDATA:
case XmlNodeType.Comment:
case XmlNodeType.Text:
case XmlNodeType.EntityReference:
case XmlNodeType.EndEntity:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.Whitespace:
{
this.xmlTokens.Add(NodeType, Value);
break;
}
case XmlNodeType.EndElement:
{
this.xmlTokens.Add(NodeType, Value);
if (--this.depth == 0)
{
this.recordDone = true;
}
break;
}
case XmlNodeType.DocumentType:
case XmlNodeType.XmlDeclaration:
{
break;
}
default:
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.UnsupportedNodeTypeInReader,
base.InnerReader.NodeType, base.InnerReader.Name)));
}
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (this.disposed)
{
return;
}
if (disposing)
{
//
// Free all of our managed resources
//
if (this.contentReader != null)
{
this.contentReader.Dispose();
this.contentReader = null;
}
if (this.contentStream != null)
{
this.contentStream.Dispose();
this.contentStream = null;
}
}
// Free native resources, if any.
this.disposed = true;
}
}
sealed internal class XmlTokenStream : ISecurityElement
{
int count;
XmlTokenEntry[] entries;
string excludedElement;
int? excludedElementDepth;
string excludedElementNamespace;
public XmlTokenStream(int initialSize)
{
if (initialSize < 1)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("initialSize", SR.GetString(SR.ValueMustBeGreaterThanZero)));
}
this.entries = new XmlTokenEntry[initialSize];
}
// This constructor is used by the Trim method to reduce the size of the XmlTokenEntry array to the minimum required.
public XmlTokenStream(XmlTokenStream other)
{
this.count = other.count;
this.excludedElement = other.excludedElement;
this.excludedElementDepth = other.excludedElementDepth;
this.excludedElementNamespace = other.excludedElementNamespace;
this.entries = new XmlTokenEntry[this.count];
Array.Copy(other.entries, this.entries, this.count);
}
public void Add(XmlNodeType type, string value)
{
EnsureCapacityToAdd();
this.entries[this.count++].Set(type, value);
}
public void AddAttribute(string prefix, string localName, string namespaceUri, string value)
{
EnsureCapacityToAdd();
this.entries[this.count++].SetAttribute(prefix, localName, namespaceUri, value);
}
public void AddElement(string prefix, string localName, string namespaceUri, bool isEmptyElement)
{
EnsureCapacityToAdd();
this.entries[this.count++].SetElement(prefix, localName, namespaceUri, isEmptyElement);
}
void EnsureCapacityToAdd()
{
if (this.count == this.entries.Length)
{
XmlTokenEntry[] newBuffer = new XmlTokenEntry[this.entries.Length * 2];
Array.Copy(this.entries, 0, newBuffer, 0, this.count);
this.entries = newBuffer;
}
}
public void SetElementExclusion(string excludedElement, string excludedElementNamespace)
{
SetElementExclusion(excludedElement, excludedElementNamespace, null);
}
public void SetElementExclusion(string excludedElement, string excludedElementNamespace, int? excludedElementDepth)
{
this.excludedElement = excludedElement;
this.excludedElementDepth = excludedElementDepth;
this.excludedElementNamespace = excludedElementNamespace;
}
/// <summary>
/// Free unneeded entries from array
/// </summary>
/// <returns></returns>
public XmlTokenStream Trim()
{
return new XmlTokenStream(this);
}
public XmlTokenStreamWriter GetWriter()
{
return new XmlTokenStreamWriter( entries, count, excludedElement, excludedElementDepth, excludedElementNamespace );
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
this.GetWriter().WriteTo(writer, dictionaryManager);
}
bool ISecurityElement.HasId
{
get { return false; }
}
string ISecurityElement.Id
{
get { return null; }
}
internal class XmlTokenStreamWriter : ISecurityElement
{
XmlTokenEntry[] entries;
int count;
int position;
string excludedElement;
int? excludedElementDepth;
string excludedElementNamespace;
public XmlTokenStreamWriter(XmlTokenEntry[] entries,
int count,
string excludedElement,
int? excludedElementDepth,
string excludedElementNamespace)
{
if (entries == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("entries");
}
this.entries = entries;
this.count = count;
this.excludedElement = excludedElement;
this.excludedElementDepth = excludedElementDepth;
this.excludedElementNamespace = excludedElementNamespace;
}
public int Count
{
get { return this.count; }
}
public int Position
{
get { return this.position; }
}
public XmlNodeType NodeType
{
get { return this.entries[this.position].nodeType; }
}
public bool IsEmptyElement
{
get { return this.entries[this.position].IsEmptyElement; }
}
public string Prefix
{
get { return this.entries[this.position].prefix; }
}
public string LocalName
{
get { return this.entries[this.position].localName; }
}
public string NamespaceUri
{
get { return this.entries[this.position].namespaceUri; }
}
public string Value
{
get { return this.entries[this.position].Value; }
}
public string ExcludedElement
{
get { return this.excludedElement; }
}
public string ExcludedElementNamespace
{
get { return this.excludedElementNamespace; }
}
bool ISecurityElement.HasId
{
get { return false; }
}
string ISecurityElement.Id
{
get { return null; }
}
public bool MoveToFirst()
{
this.position = 0;
return this.count > 0;
}
public bool MoveToFirstAttribute()
{
DiagnosticUtility.DebugAssert(this.NodeType == XmlNodeType.Element, "");
if (this.position < this.Count - 1 && this.entries[this.position + 1].nodeType == XmlNodeType.Attribute)
{
this.position++;
return true;
}
else
{
return false;
}
}
public bool MoveToNext()
{
if (this.position < this.count - 1)
{
this.position++;
return true;
}
return false;
}
public bool MoveToNextAttribute()
{
DiagnosticUtility.DebugAssert(this.NodeType == XmlNodeType.Attribute, "");
if (this.position < this.count - 1 && this.entries[this.position + 1].nodeType == XmlNodeType.Attribute)
{
this.position++;
return true;
}
else
{
return false;
}
}
public void WriteTo(XmlDictionaryWriter writer, DictionaryManager dictionaryManager)
{
if (writer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer"));
}
if (!MoveToFirst())
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.XmlTokenBufferIsEmpty)));
}
int depth = 0;
int recordedDepth = -1;
bool include = true;
do
{
switch (this.NodeType)
{
case XmlNodeType.Element:
bool isEmpty = this.IsEmptyElement;
depth++;
if (include
&& (null == excludedElementDepth || excludedElementDepth == (depth - 1))
&& this.LocalName == this.excludedElement
&& this.NamespaceUri == this.excludedElementNamespace)
{
include = false;
recordedDepth = depth;
}
if (include)
{
writer.WriteStartElement(this.Prefix, this.LocalName, this.NamespaceUri);
}
if (MoveToFirstAttribute())
{
do
{
if (include)
{
writer.WriteAttributeString(this.Prefix, this.LocalName, this.NamespaceUri, this.Value);
}
}
while (MoveToNextAttribute());
}
if (isEmpty)
{
goto case XmlNodeType.EndElement;
}
break;
case XmlNodeType.EndElement:
if (include)
{
writer.WriteEndElement();
}
else if (recordedDepth == depth)
{
include = true;
recordedDepth = -1;
}
depth--;
break;
case XmlNodeType.CDATA:
if (include)
{
writer.WriteCData(this.Value);
}
break;
case XmlNodeType.Comment:
if (include)
{
writer.WriteComment(this.Value);
}
break;
case XmlNodeType.Text:
if (include)
{
writer.WriteString(this.Value);
}
break;
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.Whitespace:
if (include)
{
writer.WriteWhitespace(this.Value);
}
break;
case XmlNodeType.DocumentType:
case XmlNodeType.XmlDeclaration:
break;
}
}
while (MoveToNext());
}
}
internal struct XmlTokenEntry
{
internal XmlNodeType nodeType;
internal string prefix;
internal string localName;
internal string namespaceUri;
string value;
public bool IsEmptyElement
{
get { return this.value == null; }
set { this.value = value ? null : ""; }
}
public string Value
{
get { return this.value; }
}
public void Set(XmlNodeType nodeType, string value)
{
this.nodeType = nodeType;
this.value = value;
}
public void SetAttribute(string prefix, string localName, string namespaceUri, string value)
{
this.nodeType = XmlNodeType.Attribute;
this.prefix = prefix;
this.localName = localName;
this.namespaceUri = namespaceUri;
this.value = value;
}
public void SetElement(string prefix, string localName, string namespaceUri, bool isEmptyElement)
{
this.nodeType = XmlNodeType.Element;
this.prefix = prefix;
this.localName = localName;
this.namespaceUri = namespaceUri;
this.IsEmptyElement = isEmptyElement;
}
}
}
}
|