|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Diagnostics
{
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using System.Xml.XPath;
using System.Globalization;
using System.Text;
using System.IO;
using System.Runtime;
// We have to put something here so that when this item appears in the
// debugger, ToString() isn't called. Calling ToString() can cause bad behavior.
[DebuggerDisplay("")]
class TraceXPathNavigator : XPathNavigator
{
const int UnlimitedSize = -1;
ElementNode root = null;
TraceNode current = null;
bool closed = false;
XPathNodeType state = XPathNodeType.Element;
int maxSize;
long currentSize;
public TraceXPathNavigator(int maxSize)
{
this.maxSize = maxSize;
this.currentSize = 0;
}
interface IMeasurable
{
int Size { get; }
}
class TraceNode
{
protected TraceNode(XPathNodeType nodeType, ElementNode parent)
{
this.nodeType = nodeType;
this.parent = parent;
}
internal XPathNodeType NodeType
{
get { return this.nodeType; }
}
XPathNodeType nodeType;
internal ElementNode parent;
}
class CommentNode : TraceNode, IMeasurable
{
internal CommentNode(string text, ElementNode parent)
: base(XPathNodeType.Comment, parent)
{
this.nodeValue = text;
}
internal string nodeValue;
public int Size
{
get
{
return this.nodeValue.Length + 8; // <!--XXX-->
}
}
}
class ElementNode : TraceNode, IMeasurable
{
int attributeIndex = 0;
int elementIndex = 0;
internal string name;
internal string prefix;
internal string xmlns;
internal List<TraceNode> childNodes = new List<TraceNode>();
internal List<AttributeNode> attributes = new List<AttributeNode>();
internal TextNode text;
internal bool movedToText = false;
internal ElementNode(string name, string prefix, ElementNode parent, string xmlns)
: base(XPathNodeType.Element, parent)
{
this.name = name;
this.prefix = prefix;
this.xmlns = xmlns;
}
internal void Add(TraceNode node)
{
this.childNodes.Add(node);
}
//This method returns all subnodes with the given path of local names. Namespaces are ignored.
//For all path elements but the last one, the first match is taken. For the last path element, all matches are returned.
internal IEnumerable<ElementNode> FindSubnodes(string[] headersPath)
{
#pragma warning disable 618
Fx.Assert(null != headersPath, "Headers path should not be null");
Fx.Assert(headersPath.Length > 0, "There should be more than one item in the headersPath array.");
#pragma warning restore 618
if (null == headersPath)
{
throw new ArgumentNullException("headersPath");
}
ElementNode node = this;
if (String.CompareOrdinal(node.name, headersPath[0]) != 0)
{
node = null;
}
int i = 0;
while (null != node && ++i < headersPath.Length)
{
#pragma warning disable 618
Fx.Assert(null != headersPath[i], "None of the elements in headersPath should be null.");
#pragma warning restore 618
ElementNode subNode = null;
if (null != node.childNodes)
{
foreach (TraceNode child in node.childNodes)
{
if (child.NodeType == XPathNodeType.Element)
{
ElementNode childNode = child as ElementNode;
if (null != childNode && 0 == String.CompareOrdinal(childNode.name, headersPath[i]))
{
if (headersPath.Length == i + 1)
{
yield return childNode;
}
else
{
subNode = childNode;
break;
}
}
}
}
}
node = subNode;
}
}
internal TraceNode MoveToNext()
{
TraceNode retval = null;
if ((this.elementIndex + 1) < this.childNodes.Count)
{
++this.elementIndex;
retval = this.childNodes[this.elementIndex];
}
return retval;
}
internal bool MoveToFirstAttribute()
{
this.attributeIndex = 0;
return null != this.attributes && this.attributes.Count > 0;
}
internal bool MoveToNextAttribute()
{
bool retval = false;
if ((this.attributeIndex + 1) < this.attributes.Count)
{
++this.attributeIndex;
retval = true;
}
return retval;
}
internal void Reset()
{
this.attributeIndex = 0;
this.elementIndex = 0;
this.movedToText = false;
if (null != this.childNodes)
{
foreach (TraceNode node in this.childNodes)
{
if (node.NodeType == XPathNodeType.Element)
{
ElementNode child = node as ElementNode;
if (child != null)
{
child.Reset();
}
}
}
}
}
internal AttributeNode CurrentAttribute
{
get
{
return this.attributes[this.attributeIndex];
}
}
public int Size
{
get
{
int size = 2 * this.name.Length + 6; //upper bound <NAME></NAME>
if (!string.IsNullOrEmpty(this.prefix))
{
size += this.prefix.Length + 1;
}
if (!string.IsNullOrEmpty(this.xmlns))
{
size += this.xmlns.Length + 9; // xmlns="xmlns"
}
return size;
}
}
}
class AttributeNode : IMeasurable
{
internal AttributeNode(string name, string prefix, string value, string xmlns)
{
this.name = name;
this.prefix = prefix;
this.nodeValue = value;
this.xmlns = xmlns;
}
internal string name;
internal string nodeValue;
internal string prefix;
internal string xmlns;
public int Size
{
get
{
int size = this.name.Length + this.nodeValue.Length + 5;
if (!string.IsNullOrEmpty(this.prefix))
{
size += this.prefix.Length + 1;
}
if (!string.IsNullOrEmpty(this.xmlns))
{
size += this.xmlns.Length + 9; //upper bound
}
return size;
}
}
}
class ProcessingInstructionNode : TraceNode, IMeasurable
{
internal ProcessingInstructionNode(string name, string text, ElementNode parent)
:
base(XPathNodeType.ProcessingInstruction, parent)
{
this.name = name;
this.text = text;
}
internal string name;
internal string text;
public int Size
{
get
{
return this.name.Length + this.text.Length + 12; //<?xml NAME="TEXT"?>
}
}
}
class TextNode : IMeasurable
{
internal TextNode(string value)
{
this.nodeValue = value;
}
internal string nodeValue;
public int Size
{
get
{
return this.nodeValue.Length;
}
}
}
internal void AddElement(string prefix, string name, string xmlns)
{
if (this.closed)
{
#pragma warning disable 618
Fx.Assert("Cannot add data to a closed document");
#pragma warning restore 618
throw new InvalidOperationException();
}
else
{
ElementNode node = new ElementNode(name, prefix, this.CurrentElement, xmlns);
if (this.current == null)
{
this.VerifySize(node);
this.root = node;
this.current = this.root;
}
else if (!this.closed)
{
this.VerifySize(node);
this.CurrentElement.Add(node);
this.current = node;
}
}
}
internal void AddProcessingInstruction(string name, string text)
{
if (this.current == null)
{
return;
}
else
{
ProcessingInstructionNode node = new ProcessingInstructionNode(name, text, this.CurrentElement);
this.VerifySize(node);
this.CurrentElement.Add(node);
}
}
internal void AddText(string value)
{
if (this.closed)
{
#pragma warning disable 618
Fx.Assert("Cannot add data to a closed document");
#pragma warning restore 618
throw new InvalidOperationException();
}
if (this.current == null)
{
return;
}
else
{
if (this.CurrentElement.text == null)
{
TextNode node = new TextNode(value);
this.VerifySize(node);
this.CurrentElement.text = node;
}
else if (!string.IsNullOrEmpty(value))
{
this.VerifySize(value);
this.CurrentElement.text.nodeValue += value;
}
}
}
internal void AddAttribute(string name, string value, string xmlns, string prefix)
{
if (this.closed)
{
#pragma warning disable 618
Fx.Assert("Cannot add data to a closed document");
#pragma warning restore 618
throw new InvalidOperationException();
}
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
AttributeNode node = new AttributeNode(name, prefix, value, xmlns);
this.VerifySize(node);
this.CurrentElement.attributes.Add(node);
}
internal void AddComment(string text)
{
if (this.closed)
{
#pragma warning disable 618
Fx.Assert("Cannot add data to a closed document");
#pragma warning restore 618
throw new InvalidOperationException();
}
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
CommentNode node = new CommentNode(text, this.CurrentElement);
this.VerifySize(node);
this.CurrentElement.Add(node);
}
internal void CloseElement()
{
if (this.closed)
{
#pragma warning disable 618
Fx.Assert("The document is already closed.");
#pragma warning restore 618
throw new InvalidOperationException();
}
else
{
this.current = this.CurrentElement.parent;
if (this.current == null)
{
this.closed = true;
}
}
}
public override string BaseURI
{
get { return String.Empty; }
}
public override XPathNavigator Clone()
{
return this;
}
public override bool IsEmptyElement
{
get
{
bool retval = true;
if (this.current != null)
{
retval = this.CurrentElement.text != null || this.CurrentElement.childNodes.Count > 0;
}
return retval;
}
}
public override bool IsSamePosition(XPathNavigator other)
{
return false;
}
[DebuggerDisplay("")]
public override string LocalName
{
get { return this.Name; }
}
public override string LookupPrefix(string ns)
{
return this.LookupPrefix(ns, this.CurrentElement);
}
string LookupPrefix(string ns, ElementNode node)
{
string retval = null;
if (string.Compare(ns, node.xmlns, StringComparison.Ordinal) == 0)
{
retval = node.prefix;
}
else
{
foreach (AttributeNode attributeNode in node.attributes)
{
if (string.Compare("xmlns", attributeNode.prefix, StringComparison.Ordinal) == 0)
{
if (string.Compare(ns, attributeNode.nodeValue, StringComparison.Ordinal) == 0)
{
retval = attributeNode.name;
break;
}
}
}
}
if (retval == null && node.parent != null)
{
retval = LookupPrefix(ns, node.parent);
}
return retval;
}
public override bool MoveTo(XPathNavigator other)
{
return false;
}
public override bool MoveToFirstAttribute()
{
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
bool retval = this.CurrentElement.MoveToFirstAttribute();
if (retval)
{
this.state = XPathNodeType.Attribute;
}
return retval;
}
public override bool MoveToFirstChild()
{
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
bool retval = false;
if (null != this.CurrentElement.childNodes && this.CurrentElement.childNodes.Count > 0)
{
this.current = this.CurrentElement.childNodes[0];
this.state = this.current.NodeType;
retval = true;
}
else if ((null == this.CurrentElement.childNodes || this.CurrentElement.childNodes.Count == 0) && this.CurrentElement.text != null)
{
this.state = XPathNodeType.Text;
this.CurrentElement.movedToText = true;
retval = true;
}
return retval;
}
public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope)
{
return false;
}
public override bool MoveToId(string id)
{
return false;
}
public override bool MoveToNext()
{
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
bool retval = false;
if (this.state != XPathNodeType.Text)
{
ElementNode parent = this.current.parent;
if (parent != null)
{
TraceNode temp = parent.MoveToNext();
if (temp == null && parent.text != null && !parent.movedToText)
{
this.state = XPathNodeType.Text;
parent.movedToText = true;
this.current = parent;
retval = true;
}
else if (temp != null)
{
this.state = temp.NodeType;
retval = true;
this.current = temp;
}
}
}
return retval;
}
public override bool MoveToNextAttribute()
{
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
bool retval = this.CurrentElement.MoveToNextAttribute();
if (retval)
{
this.state = XPathNodeType.Attribute;
}
return retval;
}
public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope)
{
return false;
}
public override bool MoveToParent()
{
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
throw new InvalidOperationException();
}
bool retval = false;
switch (this.state)
{
case XPathNodeType.Comment:
case XPathNodeType.Element:
case XPathNodeType.ProcessingInstruction:
if (this.current.parent != null)
{
this.current = this.current.parent;
this.state = this.current.NodeType;
retval = true;
}
break;
case XPathNodeType.Attribute:
this.state = XPathNodeType.Element;
retval = true;
break;
case XPathNodeType.Text:
this.state = XPathNodeType.Element;
retval = true;
break;
case XPathNodeType.Namespace:
this.state = XPathNodeType.Element;
retval = true;
break;
}
return retval;
}
public override bool MoveToPrevious()
{
return false;
}
public override void MoveToRoot()
{
this.current = this.root;
this.state = XPathNodeType.Element;
this.root.Reset();
}
[DebuggerDisplay("")]
public override string Name
{
get
{
string retval = String.Empty;
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
}
else
{
switch (this.state)
{
case XPathNodeType.Attribute:
retval = this.CurrentElement.CurrentAttribute.name;
break;
case XPathNodeType.Element:
retval = this.CurrentElement.name;
break;
case XPathNodeType.ProcessingInstruction:
retval = this.CurrentProcessingInstruction.name;
break;
}
}
return retval;
}
}
public override System.Xml.XmlNameTable NameTable
{
get { return null; }
}
[DebuggerDisplay("")]
public override string NamespaceURI
{
get
{
string retval = String.Empty;
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
}
else
{
switch (this.state)
{
case XPathNodeType.Element:
retval = this.CurrentElement.xmlns;
break;
case XPathNodeType.Attribute:
retval = this.CurrentElement.CurrentAttribute.xmlns;
break;
case XPathNodeType.Namespace:
retval = null;
break;
}
}
return retval;
}
}
[DebuggerDisplay("")]
public override XPathNodeType NodeType
{
get { return this.state; }
}
[DebuggerDisplay("")]
public override string Prefix
{
get
{
string retval = String.Empty;
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
}
else
{
switch (this.state)
{
case XPathNodeType.Element:
retval = this.CurrentElement.prefix;
break;
case XPathNodeType.Attribute:
retval = this.CurrentElement.CurrentAttribute.prefix;
break;
case XPathNodeType.Namespace:
retval = null;
break;
}
}
return retval;
}
}
CommentNode CurrentComment
{
get { return this.current as CommentNode; }
}
ElementNode CurrentElement
{
get { return this.current as ElementNode; }
}
ProcessingInstructionNode CurrentProcessingInstruction
{
get { return this.current as ProcessingInstructionNode; }
}
[DebuggerDisplay("")]
public override string Value
{
get
{
string retval = String.Empty;
if (this.current == null)
{
#pragma warning disable 618
Fx.Assert("Operation is invalid on an empty document");
#pragma warning restore 618
}
else
{
switch (this.state)
{
case XPathNodeType.Text:
retval = this.CurrentElement.text.nodeValue;
break;
case XPathNodeType.Attribute:
retval = this.CurrentElement.CurrentAttribute.nodeValue;
break;
case XPathNodeType.Comment:
retval = this.CurrentComment.nodeValue;
break;
case XPathNodeType.ProcessingInstruction:
retval = this.CurrentProcessingInstruction.text;
break;
}
}
return retval;
}
}
internal WriteState WriteState
{
get
{
WriteState retval = WriteState.Error;
if (this.current == null)
{
retval = WriteState.Start;
}
else if (this.closed)
{
retval = WriteState.Closed;
}
else
{
switch (this.state)
{
case XPathNodeType.Attribute:
retval = WriteState.Attribute;
break;
case XPathNodeType.Element:
retval = WriteState.Element;
break;
case XPathNodeType.Text:
retval = WriteState.Content;
break;
case XPathNodeType.Comment:
retval = WriteState.Content;
break;
}
}
return retval;
}
}
public override string ToString()
{
this.MoveToRoot();
StringBuilder sb = new StringBuilder();
EncodingFallbackAwareXmlTextWriter writer = new EncodingFallbackAwareXmlTextWriter(new StringWriter(sb, CultureInfo.CurrentCulture));
writer.WriteNode(this, false);
return sb.ToString();
}
void VerifySize(IMeasurable node)
{
this.VerifySize(node.Size);
}
void VerifySize(string node)
{
this.VerifySize(node.Length);
}
void VerifySize(int nodeSize)
{
if (this.maxSize != TraceXPathNavigator.UnlimitedSize)
{
if (this.currentSize + nodeSize > this.maxSize)
{
throw new PlainXmlWriter.MaxSizeExceededException();
}
}
this.currentSize += nodeSize;
}
public void RemovePii(string[][] paths)
{
#pragma warning disable 618
Fx.Assert(null != paths, "");
#pragma warning restore 618
if (paths == null)
{
throw new ArgumentNullException("paths");
}
foreach (string[] path in paths)
{
RemovePii(path);
}
}
public void RemovePii(string[] path)
{
RemovePii(path, DiagnosticStrings.PiiList);
}
public void RemovePii(string[] headersPath, string[] piiList)
{
#pragma warning disable 618
Fx.Assert(null != this.root, "");
if (this.root == null)
{
throw new InvalidOperationException();
}
foreach (ElementNode node in this.root.FindSubnodes(headersPath))
{
Fx.Assert(null != node, "");
MaskSubnodes(node, piiList);
}
}
#pragma warning restore 618
static void MaskElement(ElementNode element)
{
if (null != element)
{
element.childNodes.Clear();
element.Add(new CommentNode("Removed", element));
element.text = null;
element.attributes = null;
}
}
static void MaskSubnodes(ElementNode element, string[] elementNames)
{
MaskSubnodes(element, elementNames, false);
}
static void MaskSubnodes(ElementNode element, string[] elementNames, bool processNodeItself)
{
#pragma warning disable 618
Fx.Assert(null != elementNames, "");
#pragma warning restore 618
if (elementNames == null)
{
throw new ArgumentNullException("elementNames");
}
if (null != element)
{
bool recurse = true;
if (processNodeItself)
{
foreach (string elementName in elementNames)
{
#pragma warning disable 618
Fx.Assert(!String.IsNullOrEmpty(elementName), "");
#pragma warning restore 618
if (0 == String.CompareOrdinal(elementName, element.name))
{
MaskElement(element);
recurse = false;
break;
}
}
}
if (recurse)
{
if (null != element.childNodes)
{
foreach (ElementNode subNode in element.childNodes)
{
#pragma warning disable 618
Fx.Assert(null != subNode, "");
#pragma warning restore 618
MaskSubnodes(subNode, elementNames, true);
}
}
}
}
}
}
}
|