536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
701 lines
24 KiB
C#
701 lines
24 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="DataPointer.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
// <owner current="true" primary="false">Microsoft</owner>
|
|
//------------------------------------------------------------------------------
|
|
#pragma warning disable 618 // ignore obsolete warning about XmlDataDocument
|
|
namespace System.Xml {
|
|
using System;
|
|
using System.Data;
|
|
using System.Diagnostics;
|
|
|
|
internal sealed class DataPointer : IXmlDataVirtualNode {
|
|
private XmlDataDocument doc;
|
|
private XmlNode node;
|
|
private DataColumn column;
|
|
private bool fOnValue;
|
|
private bool bNeedFoliate = false;
|
|
private bool _isInUse;
|
|
|
|
internal DataPointer( XmlDataDocument doc, XmlNode node ) {
|
|
this.doc = doc;
|
|
this.node = node;
|
|
this.column = null;
|
|
this.fOnValue = false;
|
|
bNeedFoliate = false;
|
|
this._isInUse = true;
|
|
AssertValid();
|
|
}
|
|
|
|
internal DataPointer( DataPointer pointer ) {
|
|
this.doc = pointer.doc;
|
|
this.node = pointer.node;
|
|
this.column = pointer.column;
|
|
this.fOnValue = pointer.fOnValue;
|
|
this.bNeedFoliate = false;
|
|
this._isInUse = true;
|
|
AssertValid();
|
|
}
|
|
|
|
internal void AddPointer() {
|
|
this.doc.AddPointer( (IXmlDataVirtualNode)this );
|
|
}
|
|
|
|
// Returns the row element of the region that the pointer points into
|
|
private XmlBoundElement GetRowElement() {
|
|
//AssertValid();
|
|
|
|
XmlBoundElement rowElem;
|
|
if ( this.column != null ) {
|
|
rowElem = this.node as XmlBoundElement;
|
|
Debug.Assert( rowElem != null );
|
|
Debug.Assert( rowElem.Row != null );
|
|
return rowElem;
|
|
}
|
|
|
|
doc.Mapper.GetRegion( this.node, out rowElem );
|
|
return rowElem;
|
|
}
|
|
|
|
private DataRow Row {
|
|
get {
|
|
//AssertValid();
|
|
XmlBoundElement rowElem = GetRowElement();
|
|
if ( rowElem == null )
|
|
return null;
|
|
|
|
Debug.Assert( rowElem.Row != null );
|
|
return rowElem.Row;
|
|
}
|
|
}
|
|
|
|
private static bool IsFoliated( XmlNode node ) {
|
|
if (node != null && node is XmlBoundElement)
|
|
return((XmlBoundElement)node).IsFoliated;
|
|
return true;
|
|
}
|
|
|
|
internal void MoveTo( DataPointer pointer ) {
|
|
AssertValid();
|
|
// You should not move outside of this document
|
|
Debug.Assert( node == this.doc || node.OwnerDocument == this.doc );
|
|
|
|
this.doc = pointer.doc;
|
|
this.node = pointer.node;
|
|
this.column = pointer.column;
|
|
this.fOnValue = pointer.fOnValue;
|
|
AssertValid();
|
|
}
|
|
private void MoveTo( XmlNode node ) {
|
|
//AssertValid();
|
|
// You should not move outside of this document
|
|
Debug.Assert( node == this.doc || node.OwnerDocument == this.doc );
|
|
|
|
this.node = node;
|
|
this.column = null;
|
|
this.fOnValue = false;
|
|
AssertValid();
|
|
}
|
|
|
|
private void MoveTo( XmlNode node, DataColumn column, bool fOnValue ) {
|
|
//AssertValid();
|
|
// You should not move outside of this document
|
|
Debug.Assert( node == this.doc || node.OwnerDocument == this.doc );
|
|
|
|
this.node = node;
|
|
this.column = column;
|
|
this.fOnValue = fOnValue;
|
|
AssertValid();
|
|
}
|
|
|
|
private DataColumn NextColumn( DataRow row, DataColumn col, bool fAttribute, bool fNulls ) {
|
|
if (row.RowState == DataRowState.Deleted)
|
|
return null;
|
|
|
|
DataTable table = row.Table;
|
|
DataColumnCollection columns = table.Columns;
|
|
int iColumn = (col != null) ? col.Ordinal + 1 : 0;
|
|
int cColumns = columns.Count;
|
|
DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
|
|
|
|
for (; iColumn < cColumns; iColumn++) {
|
|
DataColumn c = columns[iColumn];
|
|
if (!doc.IsNotMapped( c ) && (c.ColumnMapping == MappingType.Attribute) == fAttribute && (fNulls || ! Convert.IsDBNull( row[c, rowVersion] ) ) )
|
|
return c;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private DataColumn NthColumn( DataRow row, bool fAttribute, int iColumn, bool fNulls ) {
|
|
DataColumn c = null;
|
|
while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) {
|
|
if (iColumn == 0)
|
|
return c;
|
|
|
|
iColumn = checked((int)iColumn-1);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private int ColumnCount( DataRow row, bool fAttribute, bool fNulls ) {
|
|
DataColumn c = null;
|
|
int count = 0;
|
|
while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) {
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
internal bool MoveToFirstChild() {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node == null)
|
|
return false;
|
|
|
|
if (column != null) {
|
|
if (fOnValue)
|
|
return false;
|
|
|
|
fOnValue = true;
|
|
return true;
|
|
}
|
|
else if (!IsFoliated( node )) {
|
|
// find virtual column elements first
|
|
DataColumn c = NextColumn( Row, null, false, false );
|
|
if (c != null) {
|
|
MoveTo( node, c, doc.IsTextOnly(c) );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// look for anything
|
|
XmlNode n = doc.SafeFirstChild( node );
|
|
if (n != null) {
|
|
MoveTo(n);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal bool MoveToNextSibling() {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node != null) {
|
|
if (column != null) {
|
|
if (fOnValue && !doc.IsTextOnly(column))
|
|
return false;
|
|
|
|
DataColumn c = NextColumn( Row, column, false, false );
|
|
if (c != null) {
|
|
MoveTo( this.node, c, false );
|
|
return true;
|
|
}
|
|
|
|
XmlNode n = doc.SafeFirstChild( node );
|
|
if (n != null) {
|
|
MoveTo( n );
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
XmlNode n = doc.SafeNextSibling( node );
|
|
if (n != null) {
|
|
MoveTo(n);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal bool MoveToParent() {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node != null) {
|
|
if (column != null) {
|
|
if (fOnValue && !doc.IsTextOnly(column)) {
|
|
MoveTo( node, column, false );
|
|
return true;
|
|
}
|
|
|
|
if (column.ColumnMapping != MappingType.Attribute) {
|
|
MoveTo( node, null, false );
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
XmlNode n = node.ParentNode;
|
|
if (n != null) {
|
|
MoveTo(n);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal bool MoveToOwnerElement() {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node != null) {
|
|
if (column != null) {
|
|
if (fOnValue || doc.IsTextOnly(column) || column.ColumnMapping != MappingType.Attribute)
|
|
return false;
|
|
|
|
MoveTo( node, null, false );
|
|
return true;
|
|
}
|
|
else if (node.NodeType == XmlNodeType.Attribute) {
|
|
XmlNode n = ((XmlAttribute)node).OwnerElement;
|
|
if (n != null) {
|
|
MoveTo( n, null, false );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
internal int AttributeCount {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node != null) {
|
|
if (column == null && node.NodeType == XmlNodeType.Element) {
|
|
if (!IsFoliated( node )) {
|
|
return ColumnCount( Row, true, false );
|
|
}
|
|
else
|
|
return node.Attributes.Count;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
internal bool MoveToAttribute( int i ) {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if ( i < 0 )
|
|
return false;
|
|
if (node != null) {
|
|
if ((column == null || column.ColumnMapping == MappingType.Attribute) && node.NodeType == XmlNodeType.Element) {
|
|
if (!IsFoliated( node )) {
|
|
DataColumn c = NthColumn( Row, true, i, false );
|
|
if (c != null) {
|
|
MoveTo( node, c, false );
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
XmlNode n = node.Attributes.Item(i);
|
|
if (n != null) {
|
|
MoveTo( n, null, false );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal XmlNodeType NodeType {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (this.node == null) {
|
|
return XmlNodeType.None;
|
|
}
|
|
else if (this.column == null) {
|
|
return this.node.NodeType;
|
|
}
|
|
else if (this.fOnValue) {
|
|
return XmlNodeType.Text;
|
|
}
|
|
else if (this.column.ColumnMapping == MappingType.Attribute) {
|
|
return XmlNodeType.Attribute;
|
|
}
|
|
else {
|
|
return XmlNodeType.Element;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal string LocalName {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (this.node == null) {
|
|
return string.Empty;
|
|
}else if (this.column == null) {
|
|
String name = node.LocalName;
|
|
Debug.Assert( name != null );
|
|
if ( IsLocalNameEmpty( this.node.NodeType ) )
|
|
return String.Empty;
|
|
return name;
|
|
}
|
|
else if (this.fOnValue) {
|
|
return String.Empty;
|
|
}
|
|
else {
|
|
return doc.NameTable.Add(column.EncodedColumnName);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal string NamespaceURI {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (this.node == null) {
|
|
return string.Empty;
|
|
}
|
|
else if (this.column == null) {
|
|
return node.NamespaceURI;
|
|
}
|
|
else if (this.fOnValue) {
|
|
return string.Empty;
|
|
}
|
|
else {
|
|
return doc.NameTable.Add(column.Namespace);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal string Name {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (this.node == null) {
|
|
return string.Empty;
|
|
}
|
|
else if (this.column == null) {
|
|
String name = node.Name;
|
|
//Again it could be String.Empty at null position
|
|
Debug.Assert( name != null );
|
|
if ( IsLocalNameEmpty( this.node.NodeType ) )
|
|
return String.Empty;
|
|
return name;
|
|
}
|
|
else {
|
|
string prefix = Prefix;
|
|
string lname = LocalName;
|
|
if (prefix != null && prefix.Length > 0) {
|
|
if (lname != null && lname.Length > 0) {
|
|
return doc.NameTable.Add( prefix + ":" + lname );
|
|
}
|
|
else {
|
|
return prefix;
|
|
}
|
|
}
|
|
else {
|
|
return lname;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
internal string Prefix {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (this.node == null) {
|
|
return string.Empty;
|
|
}
|
|
else if (this.column == null) {
|
|
return node.Prefix;
|
|
}
|
|
else {
|
|
return string.Empty;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal string Value {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (this.node == null) {
|
|
return null;
|
|
}
|
|
else if (this.column == null) {
|
|
return this.node.Value;
|
|
}
|
|
else if (this.column.ColumnMapping == MappingType.Attribute || this.fOnValue) {
|
|
DataRow row = this.Row;
|
|
DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
|
|
object value = row[ this.column, rowVersion ];
|
|
if ( ! Convert.IsDBNull( value ) )
|
|
return this.column.ConvertObjectToXml( value );
|
|
return null;
|
|
}
|
|
else {
|
|
// column element has no value
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IXmlDataVirtualNode.IsOnNode( XmlNode nodeToCheck ) {
|
|
RealFoliate();
|
|
return nodeToCheck == this.node;
|
|
}
|
|
|
|
bool IXmlDataVirtualNode.IsOnColumn( DataColumn col ) {
|
|
RealFoliate();
|
|
return col == this.column;
|
|
}
|
|
|
|
internal XmlNode GetNode() {
|
|
return this.node;
|
|
}
|
|
|
|
internal bool IsEmptyElement {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node != null && column == null) {
|
|
//
|
|
if (node.NodeType == XmlNodeType.Element) {
|
|
return((XmlElement)node).IsEmpty;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal bool IsDefault {
|
|
get {
|
|
RealFoliate();
|
|
AssertValid();
|
|
if (node != null && column == null && node.NodeType == XmlNodeType.Attribute) {
|
|
return !((XmlAttribute)node).Specified;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void IXmlDataVirtualNode.OnFoliated( XmlNode foliatedNode ) {
|
|
// update the pointer if the element node has been foliated
|
|
if (node == foliatedNode) {
|
|
// if already on this node, nothing to do!
|
|
if (column == null)
|
|
return;
|
|
bNeedFoliate = true;
|
|
}
|
|
}
|
|
|
|
internal void RealFoliate() {
|
|
if ( !bNeedFoliate )
|
|
return;
|
|
|
|
XmlNode n = null;
|
|
|
|
if (doc.IsTextOnly( column )) {
|
|
n = node.FirstChild;
|
|
}
|
|
else {
|
|
if (column.ColumnMapping == MappingType.Attribute) {
|
|
n = node.Attributes.GetNamedItem( column.EncodedColumnName, column.Namespace );
|
|
}
|
|
else {
|
|
for (n = node.FirstChild; n != null; n = n.NextSibling) {
|
|
if (n.LocalName == column.EncodedColumnName && n.NamespaceURI == column.Namespace)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (n != null && fOnValue)
|
|
n = n.FirstChild;
|
|
}
|
|
|
|
if (n == null)
|
|
throw new InvalidOperationException(Res.GetString(Res.DataDom_Foliation));
|
|
|
|
// Cannot use MoveTo( n ); b/c the initial state for MoveTo is invalid (region is foliated but this is not)
|
|
this.node = n;
|
|
this.column = null;
|
|
this.fOnValue = false;
|
|
AssertValid();
|
|
|
|
bNeedFoliate = false;
|
|
}
|
|
|
|
//for the 6 properties below, only when the this.column == null that the nodetype could be XmlDeclaration node
|
|
internal String PublicId {
|
|
get {
|
|
XmlNodeType nt = NodeType;
|
|
switch ( nt ) {
|
|
case XmlNodeType.DocumentType : {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlDocumentType ) (this.node)).PublicId;
|
|
}
|
|
case XmlNodeType.Entity : {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlEntity ) (this.node)).PublicId;
|
|
}
|
|
case XmlNodeType.Notation : {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlNotation ) (this.node)).PublicId;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal String SystemId {
|
|
get {
|
|
XmlNodeType nt = NodeType;
|
|
switch ( nt ) {
|
|
case XmlNodeType.DocumentType : {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlDocumentType ) (this.node)).SystemId;
|
|
}
|
|
case XmlNodeType.Entity : {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlEntity ) (this.node)).SystemId;
|
|
}
|
|
case XmlNodeType.Notation : {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlNotation ) (this.node)).SystemId;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal String InternalSubset {
|
|
get {
|
|
if ( NodeType == XmlNodeType.DocumentType ) {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlDocumentType ) (this.node)).InternalSubset;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal XmlDeclaration Declaration {
|
|
get {
|
|
XmlNode child = doc.SafeFirstChild(doc);
|
|
if ( child != null && child.NodeType == XmlNodeType.XmlDeclaration )
|
|
return (XmlDeclaration)child;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal String Encoding {
|
|
get {
|
|
if ( NodeType == XmlNodeType.XmlDeclaration ) {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlDeclaration ) (this.node)).Encoding;
|
|
} else if ( NodeType == XmlNodeType.Document ) {
|
|
XmlDeclaration dec = Declaration;
|
|
if ( dec != null )
|
|
return dec.Encoding;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal String Standalone {
|
|
get {
|
|
if ( NodeType == XmlNodeType.XmlDeclaration ) {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlDeclaration ) (this.node)).Standalone;
|
|
} else if ( NodeType == XmlNodeType.Document ) {
|
|
XmlDeclaration dec = Declaration;
|
|
if ( dec != null )
|
|
return dec.Standalone;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal String Version {
|
|
get {
|
|
if ( NodeType == XmlNodeType.XmlDeclaration ) {
|
|
Debug.Assert( this.column == null );
|
|
return ( ( XmlDeclaration ) (this.node)).Version;
|
|
} else if ( NodeType == XmlNodeType.Document ) {
|
|
XmlDeclaration dec = Declaration;
|
|
if ( dec != null )
|
|
return dec.Version;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
private void AssertValid() {
|
|
// This pointer must be int the document list
|
|
if ( this.column != null ) {
|
|
// We must be on a de-foliated region
|
|
XmlBoundElement rowElem = this.node as XmlBoundElement;
|
|
Debug.Assert( rowElem != null );
|
|
|
|
DataRow row = rowElem.Row;
|
|
Debug.Assert( row != null );
|
|
|
|
ElementState state = rowElem.ElementState;
|
|
Debug.Assert( state == ElementState.Defoliated, "Region is accessed using column, but it's state is FOLIATED" );
|
|
|
|
// We cannot be on a column for which the value is DBNull
|
|
DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
|
|
Debug.Assert( ! Convert.IsDBNull( row[ this.column, rowVersion ] ) );
|
|
|
|
// If we are on the Text column, we should always have fOnValue == true
|
|
Debug.Assert( (this.column.ColumnMapping == MappingType.SimpleContent) ? (this.fOnValue == true) : true );
|
|
}
|
|
}
|
|
|
|
bool IXmlDataVirtualNode.IsInUse() {
|
|
return _isInUse;
|
|
}
|
|
|
|
internal void SetNoLongerUse() {
|
|
this.node = null;
|
|
this.column = null;
|
|
this.fOnValue = false;
|
|
this.bNeedFoliate = false;
|
|
this._isInUse = false;
|
|
}
|
|
|
|
}
|
|
}
|