|
//------------------------------------------------------------------------------
// <copyright file="XmlNodeReader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Xml
{
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Schema;
using System.Globalization;
internal class XmlNodeReaderNavigator {
XmlNode curNode;
XmlNode elemNode;
XmlNode logNode;
int attrIndex;
int logAttrIndex;
//presave these 2 variables since they shouldn't change.
XmlNameTable nameTable;
XmlDocument doc;
int nAttrInd; //used to identify virtual attributes of DocumentType node and XmlDeclaration node
const String strPublicID = "PUBLIC";
const String strSystemID = "SYSTEM";
const String strVersion = "version";
const String strStandalone = "standalone";
const String strEncoding = "encoding";
//caching variables for perf reasons
int nDeclarationAttrCount;
int nDocTypeAttrCount;
//variables for roll back the moves
int nLogLevel;
int nLogAttrInd;
bool bLogOnAttrVal;
bool bCreatedOnAttribute;
internal struct VirtualAttribute {
internal String name;
internal String value;
internal VirtualAttribute(String name, String value) {
this.name = name;
this.value = value;
}
};
internal VirtualAttribute [] decNodeAttributes = {
new VirtualAttribute( null, null ),
new VirtualAttribute( null, null ),
new VirtualAttribute( null, null )
};
internal VirtualAttribute [] docTypeNodeAttributes = {
new VirtualAttribute( null, null ),
new VirtualAttribute( null, null )
};
bool bOnAttrVal;
public XmlNodeReaderNavigator( XmlNode node ) {
curNode = node;
logNode = node;
XmlNodeType nt = curNode.NodeType;
if ( nt == XmlNodeType.Attribute ) {
elemNode = null;
attrIndex = -1;
bCreatedOnAttribute = true;
}
else {
elemNode = node;
attrIndex = -1;
bCreatedOnAttribute = false;
}
//presave this for pref reason since it shouldn't change.
if ( nt == XmlNodeType.Document )
this.doc = (XmlDocument)curNode;
else
this.doc = node.OwnerDocument;
this.nameTable = doc.NameTable;
this.nAttrInd = -1;
//initialize the caching variables
this.nDeclarationAttrCount = -1;
this.nDocTypeAttrCount = -1;
this.bOnAttrVal = false;
this.bLogOnAttrVal = false;
}
public XmlNodeType NodeType {
get {
XmlNodeType nt = curNode.NodeType;
if ( nAttrInd != -1 ) {
Debug.Assert( nt == XmlNodeType.XmlDeclaration || nt == XmlNodeType.DocumentType );
if ( this.bOnAttrVal )
return XmlNodeType.Text;
else
return XmlNodeType.Attribute;
}
return nt;
}
}
public String NamespaceURI {
get { return curNode.NamespaceURI; }
}
public String Name {
get {
if ( nAttrInd != -1 ) {
Debug.Assert( curNode.NodeType == XmlNodeType.XmlDeclaration || curNode.NodeType == XmlNodeType.DocumentType );
if ( this.bOnAttrVal )
return String.Empty; //Text node's name is String.Empty
else {
Debug.Assert( nAttrInd >= 0 && nAttrInd < AttributeCount );
if ( curNode.NodeType == XmlNodeType.XmlDeclaration )
return decNodeAttributes[nAttrInd].name;
else
return docTypeNodeAttributes[nAttrInd].name;
}
}
if ( IsLocalNameEmpty ( curNode.NodeType ) )
return String.Empty;
return curNode.Name;
}
}
public String LocalName {
get {
if ( nAttrInd != -1 )
//for the nodes in this case, their LocalName should be the same as their name
return Name;
if ( IsLocalNameEmpty( curNode.NodeType ))
return String.Empty;
return curNode.LocalName;
}
}
internal bool IsOnAttrVal {
get {
return this.bOnAttrVal;
}
}
internal XmlNode OwnerElementNode {
get {
if( this.bCreatedOnAttribute )
return null;
return this.elemNode;
}
}
internal bool CreatedOnAttribute {
get {
return this.bCreatedOnAttribute;
}
}
private bool IsLocalNameEmpty ( XmlNodeType nt) {
switch ( nt ) {
case XmlNodeType.None :
case XmlNodeType.Text :
case XmlNodeType.CDATA :
case XmlNodeType.Comment :
case XmlNodeType.Document :
case XmlNodeType.DocumentFragment :
case XmlNodeType.Whitespace :
case XmlNodeType.SignificantWhitespace :
case XmlNodeType.EndElement :
case XmlNodeType.EndEntity :
return true;
case XmlNodeType.Element :
case XmlNodeType.Attribute :
case XmlNodeType.EntityReference :
case XmlNodeType.Entity :
case XmlNodeType.ProcessingInstruction :
case XmlNodeType.DocumentType :
case XmlNodeType.Notation :
case XmlNodeType.XmlDeclaration :
return false;
default :
return true;
}
}
public String Prefix {
get { return curNode.Prefix; }
}
public bool HasValue {
//In DOM, DocumentType node and XmlDeclaration node doesn't value
//In XPathNavigator, XmlDeclaration node's value is its InnerText; DocumentType doesn't have value
//In XmlReader, DocumentType node's value is its InternalSubset which is never null ( at least String.Empty )
get {
if ( nAttrInd != -1 ) {
//Pointing at the one of virtual attributes of Declaration or DocumentType nodes
Debug.Assert( curNode.NodeType == XmlNodeType.XmlDeclaration || curNode.NodeType == XmlNodeType.DocumentType );
Debug.Assert( nAttrInd >= 0 && nAttrInd < AttributeCount );
return true;
}
if ( curNode.Value != null || curNode.NodeType == XmlNodeType.DocumentType )
return true;
return false;
}
}
public String Value {
//See comments in HasValue
get {
String retValue = null;
XmlNodeType nt = curNode.NodeType;
if ( nAttrInd != -1 ) {
//Pointing at the one of virtual attributes of Declaration or DocumentType nodes
Debug.Assert( nt == XmlNodeType.XmlDeclaration || nt == XmlNodeType.DocumentType );
Debug.Assert( nAttrInd >= 0 && nAttrInd < AttributeCount );
if ( curNode.NodeType == XmlNodeType.XmlDeclaration )
return decNodeAttributes[nAttrInd].value;
else
return docTypeNodeAttributes[nAttrInd].value;
}
if ( nt == XmlNodeType.DocumentType )
retValue = ((XmlDocumentType)curNode).InternalSubset; //in this case nav.Value will be null
else if ( nt == XmlNodeType.XmlDeclaration ) {
StringBuilder strb = new StringBuilder(String.Empty);
if ( nDeclarationAttrCount == -1 )
InitDecAttr();
for ( int i = 0; i < nDeclarationAttrCount; i++ ) {
strb.Append(decNodeAttributes[i].name + "=\"" +decNodeAttributes[i].value + "\"");
if( i != ( nDeclarationAttrCount-1 ) )
strb.Append( " " );
}
retValue = strb.ToString();
} else
retValue = curNode.Value;
return ( retValue == null )? String.Empty : retValue;
}
}
public String BaseURI {
get { return curNode.BaseURI; }
}
public XmlSpace XmlSpace {
get { return curNode.XmlSpace; }
}
public String XmlLang {
get { return curNode.XmlLang; }
}
public bool IsEmptyElement {
get {
if (curNode.NodeType == XmlNodeType.Element) {
return((XmlElement)curNode).IsEmpty;
}
return false;
}
}
public bool IsDefault {
get {
if (curNode.NodeType == XmlNodeType.Attribute) {
return !((XmlAttribute)curNode).Specified;
}
return false;
}
}
public IXmlSchemaInfo SchemaInfo {
get {
return curNode.SchemaInfo;
}
}
public XmlNameTable NameTable {
get { return nameTable; }
}
public int AttributeCount {
get {
if( this.bCreatedOnAttribute )
return 0;
XmlNodeType nt = curNode.NodeType;
if ( nt == XmlNodeType.Element )
return ((XmlElement)curNode).Attributes.Count;
else if ( nt == XmlNodeType.Attribute
|| ( this.bOnAttrVal && nt != XmlNodeType.XmlDeclaration && nt != XmlNodeType.DocumentType ) )
return elemNode.Attributes.Count;
else if ( nt == XmlNodeType.XmlDeclaration ) {
if ( nDeclarationAttrCount != -1 )
return nDeclarationAttrCount;
InitDecAttr();
return nDeclarationAttrCount;
} else if ( nt == XmlNodeType.DocumentType ) {
if ( nDocTypeAttrCount != -1 )
return nDocTypeAttrCount;
InitDocTypeAttr();
return nDocTypeAttrCount;
}
return 0;
}
}
private void CheckIndexCondition(int attributeIndex) {
if (attributeIndex < 0 || attributeIndex >= AttributeCount) {
throw new ArgumentOutOfRangeException( "attributeIndex" );
}
}
//8 functions below are the helper functions to deal with virtual attributes of XmlDeclaration nodes and DocumentType nodes.
private void InitDecAttr() {
int i = 0;
String strTemp = doc.Version;
if ( strTemp != null && strTemp.Length != 0 ) {
decNodeAttributes[i].name = strVersion;
decNodeAttributes[i].value = strTemp;
i++;
}
strTemp = doc.Encoding;
if ( strTemp != null && strTemp.Length != 0 ) {
decNodeAttributes[i].name = strEncoding;
decNodeAttributes[i].value = strTemp;
i++;
}
strTemp = doc.Standalone;
if ( strTemp != null && strTemp.Length != 0 ) {
decNodeAttributes[i].name = strStandalone;
decNodeAttributes[i].value = strTemp;
i++;
}
nDeclarationAttrCount = i;
}
public String GetDeclarationAttr( XmlDeclaration decl, String name ) {
//PreCondition: curNode is pointing at Declaration node or one of its virtual attributes
if ( name == strVersion )
return decl.Version;
if ( name == strEncoding )
return decl.Encoding;
if ( name == strStandalone )
return decl.Standalone;
return null;
}
public String GetDeclarationAttr( int i ) {
if ( nDeclarationAttrCount == -1 )
InitDecAttr();
return decNodeAttributes[i].value;
}
public int GetDecAttrInd( String name ) {
if ( nDeclarationAttrCount == -1 )
InitDecAttr();
for ( int i = 0 ; i < nDeclarationAttrCount; i++ ) {
if ( decNodeAttributes[i].name == name )
return i;
}
return -1;
}
private void InitDocTypeAttr() {
int i = 0;
XmlDocumentType docType = doc.DocumentType;
if ( docType == null ) {
nDocTypeAttrCount = 0;
return;
}
String strTemp = docType.PublicId;
if ( strTemp != null ) {
docTypeNodeAttributes[i].name = strPublicID;
docTypeNodeAttributes[i].value = strTemp;
i++;
}
strTemp = docType.SystemId;
if ( strTemp != null ) {
docTypeNodeAttributes[i].name = strSystemID;
docTypeNodeAttributes[i].value = strTemp;
i++;
}
nDocTypeAttrCount = i;
}
public String GetDocumentTypeAttr ( XmlDocumentType docType, String name ) {
//PreCondition: nav is pointing at DocumentType node or one of its virtual attributes
if ( name == strPublicID )
return docType.PublicId;
if ( name == strSystemID )
return docType.SystemId;
return null;
}
public String GetDocumentTypeAttr( int i ) {
if ( nDocTypeAttrCount == -1 )
InitDocTypeAttr();
return docTypeNodeAttributes[i].value;
}
public int GetDocTypeAttrInd( String name ) {
if ( nDocTypeAttrCount == -1 )
InitDocTypeAttr();
for ( int i = 0 ; i < nDocTypeAttrCount; i++ ) {
if ( docTypeNodeAttributes[i].name == name )
return i;
}
return -1;
}
private String GetAttributeFromElement( XmlElement elem, String name ) {
XmlAttribute attr = elem.GetAttributeNode( name );
if ( attr != null )
return attr.Value;
return null;
}
public String GetAttribute( String name ) {
if( this.bCreatedOnAttribute )
return null;
switch ( curNode.NodeType ) {
case XmlNodeType.Element:
return GetAttributeFromElement((XmlElement)curNode, name);
case XmlNodeType.Attribute :
return GetAttributeFromElement((XmlElement)elemNode, name);
case XmlNodeType.XmlDeclaration:
return GetDeclarationAttr( (XmlDeclaration)curNode, name );
case XmlNodeType.DocumentType:
return GetDocumentTypeAttr( (XmlDocumentType)curNode, name );
}
return null;
}
private String GetAttributeFromElement( XmlElement elem, String name, String ns ) {
XmlAttribute attr = elem.GetAttributeNode( name, ns );
if ( attr != null )
return attr.Value;
return null;
}
public String GetAttribute( String name, String ns ) {
if( this.bCreatedOnAttribute )
return null;
switch ( curNode.NodeType ) {
case XmlNodeType.Element:
return GetAttributeFromElement((XmlElement)curNode, name, ns);
case XmlNodeType.Attribute :
return GetAttributeFromElement((XmlElement)elemNode, name, ns);
case XmlNodeType.XmlDeclaration:
return (ns.Length == 0) ? GetDeclarationAttr( (XmlDeclaration)curNode, name ) : null;
case XmlNodeType.DocumentType:
return (ns.Length == 0) ? GetDocumentTypeAttr( (XmlDocumentType)curNode, name ) : null;
}
return null;
}
public String GetAttribute( int attributeIndex ) {
if( this.bCreatedOnAttribute )
return null;
switch ( curNode.NodeType ) {
case XmlNodeType.Element:
CheckIndexCondition( attributeIndex );
return ((XmlElement)curNode).Attributes[attributeIndex].Value;
case XmlNodeType.Attribute :
CheckIndexCondition( attributeIndex );
return ((XmlElement)elemNode).Attributes[attributeIndex].Value;
case XmlNodeType.XmlDeclaration: {
CheckIndexCondition( attributeIndex );
return GetDeclarationAttr( attributeIndex );
}
case XmlNodeType.DocumentType: {
CheckIndexCondition( attributeIndex );
return GetDocumentTypeAttr( attributeIndex );
}
}
throw new ArgumentOutOfRangeException( "attributeIndex" ); //for other senario, AttributeCount is 0, i has to be out of range
}
public void LogMove( int level ) {
logNode = curNode;
nLogLevel = level;
nLogAttrInd = nAttrInd;
logAttrIndex = attrIndex;
this.bLogOnAttrVal = this.bOnAttrVal;
}
//The function has to be used in pair with ResetMove when the operation fails after LogMove() is
// called because it relies on the values of nOrigLevel, logNav and nOrigAttrInd to be acurate.
public void RollBackMove( ref int level ) {
curNode = logNode;
level = nLogLevel;
nAttrInd = nLogAttrInd;
attrIndex = logAttrIndex;
this.bOnAttrVal = this.bLogOnAttrVal;
}
private bool IsOnDeclOrDocType {
get {
XmlNodeType nt = curNode.NodeType;
return ( nt == XmlNodeType.XmlDeclaration || nt == XmlNodeType.DocumentType );
}
}
public void ResetToAttribute( ref int level ) {
//the current cursor is pointing at one of the attribute children -- this could be caused by
// the calls to ReadAttributeValue(..)
if( this.bCreatedOnAttribute )
return;
if ( this.bOnAttrVal ) {
if ( IsOnDeclOrDocType ) {
level-=2;
} else {
while ( curNode.NodeType != XmlNodeType.Attribute && ( ( curNode = curNode.ParentNode ) != null ) )
level-- ;
}
this.bOnAttrVal = false;
}
}
public void ResetMove( ref int level, ref XmlNodeType nt ) {
LogMove( level );
if( this.bCreatedOnAttribute )
return;
if ( nAttrInd != -1 ) {
Debug.Assert( IsOnDeclOrDocType );
if ( this.bOnAttrVal ) {
level--;
this.bOnAttrVal = false;
}
nLogAttrInd = nAttrInd;
level--;
nAttrInd = -1;
nt = curNode.NodeType;
return;
}
if ( this.bOnAttrVal && curNode.NodeType != XmlNodeType.Attribute )
ResetToAttribute( ref level );
if ( curNode.NodeType == XmlNodeType.Attribute ) {
curNode = ((XmlAttribute)curNode).OwnerElement;
attrIndex = -1;
level--;
nt = XmlNodeType.Element;
}
if ( curNode.NodeType == XmlNodeType.Element )
elemNode = curNode;
}
public bool MoveToAttribute( string name ) {
return MoveToAttribute( name, string.Empty );
}
private bool MoveToAttributeFromElement( XmlElement elem, String name, String ns ) {
XmlAttribute attr = null;
if( ns.Length == 0 )
attr = elem.GetAttributeNode( name );
else
attr = elem.GetAttributeNode( name, ns );
if ( attr != null ) {
this.bOnAttrVal = false;
elemNode = elem;
curNode = attr;
attrIndex = elem.Attributes.FindNodeOffsetNS(attr);
if (attrIndex != -1) {
return true;
}
}
return false;
}
public bool MoveToAttribute( string name, string namespaceURI ) {
if( this.bCreatedOnAttribute )
return false;
XmlNodeType nt = curNode.NodeType;
if ( nt == XmlNodeType.Element )
return MoveToAttributeFromElement((XmlElement)curNode, name, namespaceURI );
else if ( nt == XmlNodeType.Attribute )
return MoveToAttributeFromElement((XmlElement)elemNode, name, namespaceURI );
else if ( nt == XmlNodeType.XmlDeclaration && namespaceURI.Length == 0 ) {
if ( ( nAttrInd = GetDecAttrInd( name ) ) != -1 ) {
this.bOnAttrVal = false;
return true;
}
} else if ( nt == XmlNodeType.DocumentType && namespaceURI.Length == 0 ) {
if ( ( nAttrInd = GetDocTypeAttrInd( name ) ) != -1 ) {
this.bOnAttrVal = false;
return true;
}
}
return false;
}
public void MoveToAttribute( int attributeIndex ) {
if( this.bCreatedOnAttribute )
return;
XmlAttribute attr = null;
switch ( curNode.NodeType ) {
case XmlNodeType.Element:
CheckIndexCondition( attributeIndex );
attr = ((XmlElement)curNode).Attributes[attributeIndex];
if ( attr != null ) {
elemNode = curNode;
curNode = (XmlNode) attr;
attrIndex = attributeIndex;
}
break;
case XmlNodeType.Attribute:
CheckIndexCondition( attributeIndex );
attr = ((XmlElement)elemNode).Attributes[attributeIndex];
if ( attr != null ) {
curNode = (XmlNode) attr;
attrIndex = attributeIndex;
}
break;
case XmlNodeType.XmlDeclaration :
case XmlNodeType.DocumentType :
CheckIndexCondition( attributeIndex );
nAttrInd = attributeIndex;
break;
}
}
public bool MoveToNextAttribute( ref int level ) {
if( this.bCreatedOnAttribute )
return false;
XmlNodeType nt = curNode.NodeType;
if ( nt == XmlNodeType.Attribute ) {
if( attrIndex >= ( elemNode.Attributes.Count-1 ) )
return false;
else {
curNode = elemNode.Attributes[++attrIndex];
return true;
}
} else if ( nt == XmlNodeType.Element ) {
if ( curNode.Attributes.Count > 0 ) {
level++;
elemNode = curNode;
curNode = curNode.Attributes[0];
attrIndex = 0;
return true;
}
} else if ( nt == XmlNodeType.XmlDeclaration ) {
if ( nDeclarationAttrCount == -1 )
InitDecAttr();
nAttrInd++;
if ( nAttrInd < nDeclarationAttrCount ) {
if ( nAttrInd == 0 ) level++;
this.bOnAttrVal = false;
return true;
}
nAttrInd--;
} else if ( nt == XmlNodeType.DocumentType ) {
if ( nDocTypeAttrCount == -1 )
InitDocTypeAttr();
nAttrInd++;
if ( nAttrInd < nDocTypeAttrCount ) {
if ( nAttrInd == 0 ) level++;
this.bOnAttrVal = false;
return true;
}
nAttrInd--;
}
return false;
}
public bool MoveToParent() {
XmlNode parent = curNode.ParentNode;
if ( parent != null ) {
curNode = parent;
if( !bOnAttrVal )
attrIndex = 0;
return true;
}
return false;
}
public bool MoveToFirstChild() {
XmlNode firstChild = curNode.FirstChild;
if ( firstChild != null ) {
curNode = firstChild;
if( !bOnAttrVal )
attrIndex = -1;
return true;
}
return false;
}
private bool MoveToNextSibling( XmlNode node ) {
XmlNode nextSibling = node.NextSibling;
if ( nextSibling != null ) {
curNode = nextSibling;
if( !bOnAttrVal )
attrIndex = -1;
return true;
}
return false;
}
public bool MoveToNext() {
if ( curNode.NodeType != XmlNodeType.Attribute )
return MoveToNextSibling( curNode );
else
return MoveToNextSibling( elemNode );
}
public bool MoveToElement() {
if( this.bCreatedOnAttribute )
return false;
switch ( curNode.NodeType ) {
case XmlNodeType.Attribute :
if ( elemNode != null ) {
curNode = elemNode;
attrIndex = -1;
return true;
}
break;
case XmlNodeType.XmlDeclaration :
case XmlNodeType.DocumentType : {
if ( nAttrInd != -1 ) {
nAttrInd = -1;
return true;
}
break;
}
}
return false;
}
public String LookupNamespace(string prefix) {
if( this.bCreatedOnAttribute )
return null;
if ( prefix == "xmlns" ) {
return nameTable.Add( XmlReservedNs.NsXmlNs );
}
if ( prefix == "xml" ) {
return nameTable.Add( XmlReservedNs.NsXml );
}
// construct the name of the xmlns attribute
string attrName;
if ( prefix == null )
prefix = string.Empty;
if ( prefix.Length == 0 )
attrName = "xmlns";
else
attrName = "xmlns:" + prefix;
// walk up the XmlNode parent chain, looking for the xmlns attribute
XmlNode node = curNode;
while ( node != null ) {
if ( node.NodeType == XmlNodeType.Element ) {
XmlElement elem = (XmlElement)node;
if ( elem.HasAttributes ) {
XmlAttribute attr = elem.GetAttributeNode( attrName );
if ( attr != null ) {
return attr.Value;
}
}
}
else if ( node.NodeType == XmlNodeType.Attribute ) {
node = ((XmlAttribute)node).OwnerElement;
continue;
}
node = node.ParentNode;
}
if ( prefix.Length == 0 ) {
return string.Empty;
}
return null;
}
internal string DefaultLookupNamespace( string prefix ) {
if( !this.bCreatedOnAttribute ) {
if ( prefix == "xmlns" ) {
return nameTable.Add( XmlReservedNs.NsXmlNs );
}
if ( prefix == "xml" ) {
return nameTable.Add( XmlReservedNs.NsXml );
}
if ( prefix == string.Empty ) {
return nameTable.Add( string.Empty );
}
}
return null;
}
internal String LookupPrefix(string namespaceName) {
if( this.bCreatedOnAttribute || namespaceName == null ) {
return null;
}
if ( namespaceName == XmlReservedNs.NsXmlNs ) {
return nameTable.Add( "xmlns" );
}
if ( namespaceName == XmlReservedNs.NsXml ) {
return nameTable.Add( "xml" );
}
if ( namespaceName == string.Empty ) {
return string.Empty;
}
// walk up the XmlNode parent chain, looking for the xmlns attribute with namespaceName value
XmlNode node = curNode;
while ( node != null ) {
if ( node.NodeType == XmlNodeType.Element ) {
XmlElement elem = (XmlElement)node;
if ( elem.HasAttributes ) {
XmlAttributeCollection attrs = elem.Attributes;
for ( int i = 0; i < attrs.Count; i++ ) {
XmlAttribute a = attrs[i];
if ( a.Value == namespaceName ) {
if ( a.Prefix.Length == 0 && a.LocalName == "xmlns" ) {
if ( LookupNamespace( string.Empty ) == namespaceName ) {
return string.Empty;
}
}
else if ( a.Prefix == "xmlns" ) {
string pref = a.LocalName;
if ( LookupNamespace( pref ) == namespaceName ) {
return nameTable.Add( pref );
}
}
}
}
}
}
else if ( node.NodeType == XmlNodeType.Attribute ) {
node = ((XmlAttribute)node).OwnerElement;
continue;
}
node = node.ParentNode;
}
return null;
}
internal IDictionary<string,string> GetNamespacesInScope( XmlNamespaceScope scope ) {
Dictionary<string,string> dict = new Dictionary<string, string>();
if( this.bCreatedOnAttribute )
return dict;
// walk up the XmlNode parent chain and add all namespace declarations to the dictionary
XmlNode node = curNode;
while ( node != null ) {
if ( node.NodeType == XmlNodeType.Element ) {
XmlElement elem = (XmlElement)node;
if ( elem.HasAttributes ) {
XmlAttributeCollection attrs = elem.Attributes;
for ( int i = 0; i < attrs.Count; i++ ) {
XmlAttribute a = attrs[i];
if ( a.LocalName == "xmlns" && a.Prefix.Length == 0 ) {
if ( !dict.ContainsKey( string.Empty ) ) {
dict.Add( nameTable.Add( string.Empty ), nameTable.Add( a.Value ) );
}
}
else if ( a.Prefix == "xmlns" ) {
string localName = a.LocalName;
if ( !dict.ContainsKey( localName ) ) {
dict.Add( nameTable.Add( localName ), nameTable.Add( a.Value ) );
}
}
}
}
if ( scope == XmlNamespaceScope.Local ) {
break;
}
}
else if ( node.NodeType == XmlNodeType.Attribute ) {
node = ((XmlAttribute)node).OwnerElement;
continue;
}
node = node.ParentNode;
};
if ( scope != XmlNamespaceScope.Local ) {
if ( dict.ContainsKey( string.Empty ) && dict[string.Empty] == string.Empty ) {
dict.Remove( string.Empty );
}
if ( scope == XmlNamespaceScope.All ) {
dict.Add( nameTable.Add( "xml" ), nameTable.Add( XmlReservedNs.NsXml ) );
}
}
return dict;
}
public bool ReadAttributeValue( ref int level, ref bool bResolveEntity, ref XmlNodeType nt ) {
if ( nAttrInd != -1 ) {
Debug.Assert( curNode.NodeType == XmlNodeType.XmlDeclaration || curNode.NodeType == XmlNodeType.DocumentType );
if ( !this.bOnAttrVal ) {
this.bOnAttrVal = true;
level++;
nt = XmlNodeType.Text;
return true;
}
return false;
}
if( curNode.NodeType == XmlNodeType.Attribute ) {
XmlNode firstChild = curNode.FirstChild;
if ( firstChild != null ) {
curNode = firstChild;
nt = curNode.NodeType;
level++;
this.bOnAttrVal = true;
return true;
}
}
else if ( this.bOnAttrVal ) {
XmlNode nextSibling = null;
if ( curNode.NodeType == XmlNodeType.EntityReference && bResolveEntity ) {
//going down to ent ref node
curNode = curNode.FirstChild;
nt = curNode.NodeType;
Debug.Assert( curNode != null );
level++;
bResolveEntity = false;
return true;
}
else
nextSibling = curNode.NextSibling;
if ( nextSibling == null ) {
XmlNode parentNode = curNode.ParentNode;
//Check if its parent is entity ref node is sufficient, because in this senario, ent ref node can't have more than 1 level of children that are not other ent ref nodes
if ( parentNode != null && parentNode.NodeType == XmlNodeType.EntityReference ) {
//come back from ent ref node
curNode = parentNode;
nt = XmlNodeType.EndEntity;
level--;
return true;
}
}
if ( nextSibling != null ) {
curNode = nextSibling;
nt = curNode.NodeType;
return true;
}
else
return false;
}
return false;
}
public XmlDocument Document {
get {
return this.doc;
}
}
}
// Represents a reader that provides fast, non-cached forward only stream access
// to XML data in an XmlDocument or a specific XmlNode within an XmlDocument.
public class XmlNodeReader: XmlReader, IXmlNamespaceResolver
{
XmlNodeReaderNavigator readerNav;
XmlNodeType nodeType; // nodeType of the node that the reader is currently positioned on
int curDepth; // depth of attrNav ( also functions as reader's depth )
ReadState readState; // current reader's state
bool fEOF; // flag to show if reaches the end of file
//mark to the state that EntityReference node is supposed to be resolved
bool bResolveEntity;
bool bStartFromDocument;
bool bInReadBinary;
ReadContentAsBinaryHelper readBinaryHelper;
// Creates an instance of the XmlNodeReader class using the specified XmlNode.
public XmlNodeReader ( XmlNode node ) {
if (node == null) {
throw new ArgumentNullException("node");
}
readerNav = new XmlNodeReaderNavigator( node );
this.curDepth = 0;
readState = ReadState.Initial;
fEOF = false;
nodeType = XmlNodeType.None;
bResolveEntity = false;
bStartFromDocument = false;
}
//function returns if the reader currently in valid reading states
internal bool IsInReadingStates() {
return ( readState == ReadState.Interactive ); // || readState == ReadState.EndOfFile
}
//
// Node Properties
//
// Gets the type of the current node.
public override XmlNodeType NodeType {
get { return ( IsInReadingStates() )? nodeType : XmlNodeType.None; }
}
// Gets the name of
// the current node, including the namespace prefix.
public override string Name {
get {
if ( !IsInReadingStates() )
return String.Empty;
return readerNav.Name;
}
}
// Gets the name of the current node without the namespace prefix.
public override string LocalName {
get {
if ( !IsInReadingStates() )
return String.Empty;
return readerNav.LocalName;
}
}
// Gets the namespace URN (as defined in the W3C Namespace Specification)
// of the current namespace scope.
public override string NamespaceURI {
get {
if ( !IsInReadingStates() )
return String.Empty;
return readerNav.NamespaceURI;
}
}
// Gets the namespace prefix associated with the current node.
public override string Prefix {
get {
if ( !IsInReadingStates() )
return String.Empty;
return readerNav.Prefix;
}
}
// Gets a value indicating whether
// XmlNodeReader.Value has a value to return.
public override bool HasValue {
get {
if ( !IsInReadingStates() )
return false;
return readerNav.HasValue;
}
}
// Gets the text value of the current node.
public override string Value {
get {
if ( !IsInReadingStates() )
return String.Empty;
return readerNav.Value;
}
}
// Gets the depth of the
// current node in the XML element stack.
public override int Depth {
get { return curDepth; }
}
// Gets the base URI of the current node.
public override String BaseURI {
get { return readerNav.BaseURI; }
}
public override bool CanResolveEntity {
get { return true; }
}
// Gets a value indicating whether the current
// node is an empty element (for example, <MyElement/>.
public override bool IsEmptyElement {
get {
if ( !IsInReadingStates() )
return false;
return readerNav.IsEmptyElement;
}
}
// Gets a value indicating whether the current node is an
// attribute that was generated from the default value defined
// in the DTD or schema.
public override bool IsDefault {
get {
if ( !IsInReadingStates() )
return false;
return readerNav.IsDefault;
}
}
// Gets the current xml:space scope.
public override XmlSpace XmlSpace {
get {
if ( !IsInReadingStates() )
return XmlSpace.None;
return readerNav.XmlSpace;
}
}
// Gets the current xml:lang scope.
public override string XmlLang {
// Assume everything is in Unicode
get {
if ( !IsInReadingStates() )
return String.Empty;
return readerNav.XmlLang;
}
}
public override IXmlSchemaInfo SchemaInfo {
get {
if (!IsInReadingStates()) {
return null;
}
return readerNav.SchemaInfo;
}
}
//
// Attribute Accessors
//
// Gets the number of attributes on the current node.
public override int AttributeCount {
get {
if ( !IsInReadingStates() || nodeType == XmlNodeType.EndElement )
return 0;
return readerNav.AttributeCount;
}
}
// Gets the value of the attribute with the specified name.
public override string GetAttribute(string name) {
//if not on Attribute, only element node could have attributes
if ( !IsInReadingStates() )
return null;
return readerNav.GetAttribute( name );
}
// Gets the value of the attribute with the specified name and namespace.
public override string GetAttribute(string name, string namespaceURI) {
//if not on Attribute, only element node could have attributes
if ( !IsInReadingStates() )
return null;
String ns = ( namespaceURI == null ) ? String.Empty : namespaceURI;
return readerNav.GetAttribute( name, ns );
}
// Gets the value of the attribute with the specified index.
public override string GetAttribute(int attributeIndex) {
if ( !IsInReadingStates() )
throw new ArgumentOutOfRangeException( "attributeIndex" );
//CheckIndexCondition( i );
//Debug.Assert( nav.NodeType == XmlNodeType.Element );
return readerNav.GetAttribute( attributeIndex );
}
// Moves to the attribute with the specified name.
public override bool MoveToAttribute(string name) {
if ( !IsInReadingStates() )
return false;
readerNav.ResetMove( ref curDepth, ref nodeType );
if ( readerNav.MoveToAttribute( name ) ) { //, ref curDepth ) ) {
curDepth++;
nodeType = readerNav.NodeType;
if ( bInReadBinary ) {
FinishReadBinary();
}
return true;
}
readerNav.RollBackMove(ref curDepth);
return false;
}
// Moves to the attribute with the specified name and namespace.
public override bool MoveToAttribute(string name, string namespaceURI) {
if ( !IsInReadingStates() )
return false;
readerNav.ResetMove( ref curDepth, ref nodeType );
String ns = ( namespaceURI == null ) ? String.Empty : namespaceURI;
if ( readerNav.MoveToAttribute( name, ns ) ) { //, ref curDepth ) ) {
curDepth++;
nodeType = readerNav.NodeType;
if ( bInReadBinary ) {
FinishReadBinary();
}
return true;
}
readerNav.RollBackMove(ref curDepth);
return false;
}
// Moves to the attribute with the specified index.
public override void MoveToAttribute(int attributeIndex) {
if ( !IsInReadingStates() )
throw new ArgumentOutOfRangeException( "attributeIndex" );
readerNav.ResetMove( ref curDepth, ref nodeType );
try {
if (AttributeCount > 0) {
readerNav.MoveToAttribute( attributeIndex );
if ( bInReadBinary ) {
FinishReadBinary();
}
}
else
throw new ArgumentOutOfRangeException( "attributeIndex" );
} catch {
readerNav.RollBackMove(ref curDepth);
throw;
}
curDepth++;
nodeType = readerNav.NodeType;
}
// Moves to the first attribute.
public override bool MoveToFirstAttribute() {
if ( !IsInReadingStates() )
return false;
readerNav.ResetMove( ref curDepth, ref nodeType );
if (AttributeCount > 0) {
readerNav.MoveToAttribute( 0 );
curDepth++;
nodeType = readerNav.NodeType;
if ( bInReadBinary ) {
FinishReadBinary();
}
return true;
}
readerNav.RollBackMove( ref curDepth );
return false;
}
// Moves to the next attribute.
public override bool MoveToNextAttribute() {
if ( !IsInReadingStates() || nodeType == XmlNodeType.EndElement )
return false;
readerNav.LogMove( curDepth );
readerNav.ResetToAttribute( ref curDepth );
if ( readerNav.MoveToNextAttribute( ref curDepth ) ) {
nodeType = readerNav.NodeType;
if ( bInReadBinary ) {
FinishReadBinary();
}
return true;
}
readerNav.RollBackMove( ref curDepth );
return false;
}
// Moves to the element that contains the current attribute node.
public override bool MoveToElement() {
if ( !IsInReadingStates() )
return false;
readerNav.LogMove( curDepth );
readerNav.ResetToAttribute( ref curDepth );
if ( readerNav.MoveToElement() ) {
curDepth--;
nodeType = readerNav.NodeType;
if ( bInReadBinary ) {
FinishReadBinary();
}
return true;
}
readerNav.RollBackMove( ref curDepth );
return false;
}
//
// Moving through the Stream
//
// Reads the next node from the stream.
public override bool Read() {
return Read( false );
}
private bool Read( bool fSkipChildren ) {
if( fEOF )
return false;
if ( readState == ReadState.Initial ) {
// if nav is pointing at the document node, start with its children
// otherwise,start with the node.
if ( ( readerNav.NodeType == XmlNodeType.Document ) || ( readerNav.NodeType == XmlNodeType.DocumentFragment ) ) {
bStartFromDocument = true;
if ( !ReadNextNode(fSkipChildren) ) {
readState = ReadState.Error;
return false;
}
}
ReSetReadingMarks();
readState = ReadState.Interactive;
nodeType = readerNav.NodeType;
//_depth = 0;
curDepth = 0;
return true;
}
if ( bInReadBinary ) {
FinishReadBinary();
}
bool bRead = false;
if( ( readerNav.CreatedOnAttribute ) )
return false;
ReSetReadingMarks();
bRead = ReadNextNode(fSkipChildren);
if ( bRead ) {
return true;
} else {
if ( readState == ReadState.Initial || readState == ReadState.Interactive )
readState = ReadState.Error;
if ( readState == ReadState.EndOfFile )
nodeType = XmlNodeType.None;
return false;
}
}
private bool ReadNextNode( bool fSkipChildren ) {
if ( readState != ReadState.Interactive && readState != ReadState.Initial ) {
nodeType = XmlNodeType.None;
return false;
}
bool bDrillDown = !fSkipChildren;
XmlNodeType nt = readerNav.NodeType;
//only goes down when nav.NodeType is of element or of document at the initial state, other nav.NodeType will not be parsed down
//if nav.NodeType is of EntityReference, ResolveEntity() could be called to get the content parsed;
bDrillDown = bDrillDown
&& ( nodeType != XmlNodeType.EndElement )
&& ( nodeType != XmlNodeType.EndEntity )
&& ( nt == XmlNodeType.Element || ( nt == XmlNodeType.EntityReference && bResolveEntity ) ||
( ( ( readerNav.NodeType == XmlNodeType.Document ) || ( readerNav.NodeType == XmlNodeType.DocumentFragment ) ) && readState == ReadState.Initial) );
//first see if there are children of current node, so to move down
if ( bDrillDown ) {
if ( readerNav.MoveToFirstChild() ) {
nodeType = readerNav.NodeType;
curDepth++;
if ( bResolveEntity )
bResolveEntity = false;
return true;
} else if ( readerNav.NodeType == XmlNodeType.Element
&& !readerNav.IsEmptyElement ) {
nodeType = XmlNodeType.EndElement;
return true;
}
else if (readerNav.NodeType == XmlNodeType.EntityReference && bResolveEntity) {
bResolveEntity = false;
nodeType = XmlNodeType.EndEntity;
return true;
}
// if fails to move to it 1st Child, try to move to next below
return ReadForward( fSkipChildren );
} else {
if ( readerNav.NodeType == XmlNodeType.EntityReference && bResolveEntity ) {
//The only way to get to here is because Skip() is called directly after ResolveEntity()
// in this case, user wants to skip the first Child of EntityRef node and fSkipChildren is true
// We want to pointing to the first child node.
if (readerNav.MoveToFirstChild()) {
nodeType = readerNav.NodeType;
curDepth++;
}
else {
nodeType = XmlNodeType.EndEntity;
}
bResolveEntity = false;
return true;
}
}
return ReadForward( fSkipChildren ); //has to get the next node by moving forward
}
private void SetEndOfFile() {
fEOF = true;
readState = ReadState.EndOfFile;
nodeType = XmlNodeType.None;
}
private bool ReadAtZeroLevel(bool fSkipChildren) {
Debug.Assert( curDepth == 0 );
if ( !fSkipChildren
&& nodeType != XmlNodeType.EndElement
&& readerNav.NodeType == XmlNodeType.Element
&& !readerNav.IsEmptyElement ) {
nodeType = XmlNodeType.EndElement;
return true;
} else {
SetEndOfFile();
return false;
}
}
private bool ReadForward( bool fSkipChildren ) {
if ( readState == ReadState.Error )
return false;
if ( !bStartFromDocument && curDepth == 0 ) {
//already on top most node and we shouldn't move to next
return ReadAtZeroLevel(fSkipChildren);
}
//else either we are not on top level or we are starting from the document at the very beginning in which case
// we will need to read all the "top" most nodes
if ( readerNav.MoveToNext() ) {
nodeType = readerNav.NodeType;
return true;
} else {
//need to check its parent
if ( curDepth == 0 )
return ReadAtZeroLevel(fSkipChildren);
if ( readerNav.MoveToParent() ) {
if ( readerNav.NodeType == XmlNodeType.Element ) {
curDepth--;
nodeType = XmlNodeType.EndElement;
return true;
} else if ( readerNav.NodeType == XmlNodeType.EntityReference ) {
//coming back from entity reference node -- must be getting down through call ResolveEntity()
curDepth--;
nodeType = XmlNodeType.EndEntity;
return true;
}
return true;
}
}
return false;
}
//the function reset the marks used for ReadChars() and MoveToAttribute(...), ReadAttributeValue(...)
private void ReSetReadingMarks() {
//_attrValInd = -1;
readerNav.ResetMove( ref curDepth, ref nodeType );
//attrNav.MoveTo( nav );
//curDepth = _depth;
}
// Gets a value indicating whether the reader is positioned at the
// end of the stream.
public override bool EOF {
get { return (readState != ReadState.Closed) && fEOF; }
}
// Closes the stream, changes the XmlNodeReader.ReadState
// to Closed, and sets all the properties back to zero.
public override void Close() {
readState = ReadState.Closed;
}
// Gets the read state of the stream.
public override ReadState ReadState {
get { return readState; }
}
// Skips to the end tag of the current element.
public override void Skip() {
Read( true );
}
// Reads the contents of an element as a string.
public override string ReadString() {
if ((this.NodeType == XmlNodeType.EntityReference) && bResolveEntity) {
if (! this.Read()) {
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
}
}
return base.ReadString();
}
//
// Partial Content Read Methods
//
// Gets a value indicating whether the current node
// has any attributes.
public override bool HasAttributes {
get {
return ( AttributeCount > 0 );
}
}
//
// Nametable and Namespace Helpers
//
// Gets the XmlNameTable associated with this implementation.
public override XmlNameTable NameTable {
get { return readerNav.NameTable; }
}
// Resolves a namespace prefix in the current element's scope.
public override String LookupNamespace(string prefix) {
if ( !IsInReadingStates() )
return null;
string ns = readerNav.LookupNamespace( prefix );
if (ns != null && ns.Length == 0) {
return null;
}
return ns;
}
// Resolves the entity reference for nodes of NodeType EntityReference.
public override void ResolveEntity() {
if ( !IsInReadingStates() || ( nodeType != XmlNodeType.EntityReference ) )
throw new InvalidOperationException(Res.GetString(Res.Xnr_ResolveEntity));
bResolveEntity = true;;
}
// Parses the attribute value into one or more Text and/or
// EntityReference node types.
public override bool ReadAttributeValue() {
if ( !IsInReadingStates() )
return false;
if ( readerNav.ReadAttributeValue( ref curDepth, ref bResolveEntity, ref nodeType ) ) {
bInReadBinary = false;
return true;
}
return false;
}
public override bool CanReadBinaryContent {
get {
return true;
}
}
public override int ReadContentAsBase64( byte[] buffer, int index, int count ) {
if ( readState != ReadState.Interactive ) {
return 0;
}
// init ReadContentAsBinaryHelper when called first time
if ( !bInReadBinary ) {
readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset( readBinaryHelper, this );
}
// turn off bInReadBinary in order to have a normal Read() behavior when called from readBinaryHelper
bInReadBinary = false;
// call to the helper
int readCount = readBinaryHelper.ReadContentAsBase64( buffer, index, count );
// turn on bInReadBinary in again and return
bInReadBinary = true;
return readCount;
}
public override int ReadContentAsBinHex( byte[] buffer, int index, int count ) {
if ( readState != ReadState.Interactive ) {
return 0;
}
// init ReadContentAsBinaryHelper when called first time
if ( !bInReadBinary ) {
readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset( readBinaryHelper, this );
}
// turn off bInReadBinary in order to have a normal Read() behavior when called from readBinaryHelper
bInReadBinary = false;
// call to the helper
int readCount = readBinaryHelper.ReadContentAsBinHex( buffer, index, count );
// turn on bInReadBinary in again and return
bInReadBinary = true;
return readCount;
}
public override int ReadElementContentAsBase64( byte[] buffer, int index, int count ) {
if ( readState != ReadState.Interactive ) {
return 0;
}
// init ReadContentAsBinaryHelper when called first time
if ( !bInReadBinary ) {
readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset( readBinaryHelper, this );
}
// turn off bInReadBinary in order to have a normal Read() behavior when called from readBinaryHelper
bInReadBinary = false;
// call to the helper
int readCount = readBinaryHelper.ReadElementContentAsBase64( buffer, index, count );
// turn on bInReadBinary in again and return
bInReadBinary = true;
return readCount;
}
public override int ReadElementContentAsBinHex( byte[] buffer, int index, int count ) {
if ( readState != ReadState.Interactive ) {
return 0;
}
// init ReadContentAsBinaryHelper when called first time
if ( !bInReadBinary ) {
readBinaryHelper = ReadContentAsBinaryHelper.CreateOrReset( readBinaryHelper, this );
}
// turn off bInReadBinary in order to have a normal Read() behavior when called from readBinaryHelper
bInReadBinary = false;
// call to the helper
int readCount = readBinaryHelper.ReadElementContentAsBinHex( buffer, index, count );
// turn on bInReadBinary in again and return
bInReadBinary = true;
return readCount;
}
void FinishReadBinary() {
bInReadBinary = false;
readBinaryHelper.Finish();
}
//
// IXmlNamespaceResolver
//
IDictionary<string,string> IXmlNamespaceResolver.GetNamespacesInScope( XmlNamespaceScope scope ) {
return readerNav.GetNamespacesInScope( scope );
}
string IXmlNamespaceResolver.LookupPrefix( string namespaceName ) {
return readerNav.LookupPrefix( namespaceName );
}
String IXmlNamespaceResolver.LookupNamespace( string prefix ) {
if ( !IsInReadingStates() ) {
return readerNav.DefaultLookupNamespace( prefix );
}
string ns = readerNav.LookupNamespace( prefix );
if ( ns != null ) {
ns = readerNav.NameTable.Add( ns );
}
return ns;
}
// DTD/Schema info used by XmlReader.GetDtdSchemaInfo()
internal override IDtdInfo DtdInfo {
get {
return readerNav.Document.DtdSchemaInfo;
}
}
}
}
|