1 //------------------------------------------------------------------------------
2 // <copyright file="DataPointer.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8 #pragma warning disable 618 // ignore obsolete warning about XmlDataDocument
9 namespace System.Xml {
10     using System;
11     using System.Data;
12     using System.Diagnostics;
13 
14     internal sealed class DataPointer : IXmlDataVirtualNode {
15         private XmlDataDocument doc;
16         private XmlNode node;
17         private DataColumn column;
18         private bool fOnValue;
19         private bool bNeedFoliate = false;
20         private bool _isInUse;
21 
DataPointer( XmlDataDocument doc, XmlNode node )22         internal DataPointer( XmlDataDocument doc, XmlNode node ) {
23             this.doc = doc;
24             this.node = node;
25             this.column = null;
26             this.fOnValue = false;
27             bNeedFoliate = false;
28             this._isInUse = true;
29             AssertValid();
30         }
31 
DataPointer( DataPointer pointer )32         internal DataPointer( DataPointer pointer ) {
33             this.doc = pointer.doc;
34             this.node = pointer.node;
35             this.column = pointer.column;
36             this.fOnValue = pointer.fOnValue;
37             this.bNeedFoliate = false;
38             this._isInUse = true;
39             AssertValid();
40         }
41 
AddPointer()42         internal void AddPointer() {
43             this.doc.AddPointer( (IXmlDataVirtualNode)this );
44         }
45 
46         // Returns the row element of the region that the pointer points into
GetRowElement()47         private XmlBoundElement GetRowElement() {
48             //AssertValid();
49 
50             XmlBoundElement rowElem;
51             if ( this.column != null ) {
52                 rowElem = this.node as XmlBoundElement;
53                 Debug.Assert( rowElem != null );
54                 Debug.Assert( rowElem.Row != null );
55                 return rowElem;
56             }
57 
58             doc.Mapper.GetRegion( this.node, out rowElem );
59             return rowElem;
60         }
61 
62         private DataRow Row {
63             get {
64                 //AssertValid();
65                 XmlBoundElement rowElem = GetRowElement();
66                 if ( rowElem == null )
67                     return null;
68 
69                 Debug.Assert( rowElem.Row != null );
70                 return rowElem.Row;
71             }
72         }
73 
IsFoliated( XmlNode node )74         private static bool IsFoliated( XmlNode node ) {
75             if (node != null && node is XmlBoundElement)
76                 return((XmlBoundElement)node).IsFoliated;
77             return true;
78         }
79 
MoveTo( DataPointer pointer )80         internal void MoveTo( DataPointer pointer ) {
81             AssertValid();
82             // You should not move outside of this document
83             Debug.Assert( node == this.doc || node.OwnerDocument == this.doc );
84 
85             this.doc = pointer.doc;
86             this.node = pointer.node;
87             this.column = pointer.column;
88             this.fOnValue = pointer.fOnValue;
89             AssertValid();
90         }
MoveTo( XmlNode node )91         private void MoveTo( XmlNode node ) {
92             //AssertValid();
93             // You should not move outside of this document
94             Debug.Assert( node == this.doc || node.OwnerDocument == this.doc );
95 
96             this.node = node;
97             this.column = null;
98             this.fOnValue = false;
99             AssertValid();
100         }
101 
MoveTo( XmlNode node, DataColumn column, bool fOnValue )102         private void MoveTo( XmlNode node, DataColumn column, bool fOnValue ) {
103             //AssertValid();
104             // You should not move outside of this document
105             Debug.Assert( node == this.doc || node.OwnerDocument == this.doc );
106 
107             this.node = node;
108             this.column = column;
109             this.fOnValue = fOnValue;
110             AssertValid();
111         }
112 
NextColumn( DataRow row, DataColumn col, bool fAttribute, bool fNulls )113         private DataColumn NextColumn( DataRow row, DataColumn col, bool fAttribute, bool fNulls ) {
114             if (row.RowState == DataRowState.Deleted)
115                 return null;
116 
117             DataTable table = row.Table;
118             DataColumnCollection columns = table.Columns;
119             int iColumn = (col != null) ? col.Ordinal + 1 : 0;
120             int cColumns = columns.Count;
121             DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
122 
123             for (; iColumn < cColumns; iColumn++) {
124                 DataColumn c = columns[iColumn];
125                 if (!doc.IsNotMapped( c ) && (c.ColumnMapping == MappingType.Attribute) == fAttribute && (fNulls || ! Convert.IsDBNull( row[c, rowVersion] ) ) )
126                     return c;
127             }
128 
129             return null;
130         }
131 
NthColumn( DataRow row, bool fAttribute, int iColumn, bool fNulls )132         private DataColumn NthColumn( DataRow row, bool fAttribute, int iColumn, bool fNulls ) {
133             DataColumn c = null;
134             while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) {
135                 if (iColumn == 0)
136                     return c;
137 
138                 iColumn = checked((int)iColumn-1);
139             }
140             return null;
141         }
142 
ColumnCount( DataRow row, bool fAttribute, bool fNulls )143         private int ColumnCount( DataRow row, bool fAttribute, bool fNulls ) {
144             DataColumn c = null;
145             int count = 0;
146             while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) {
147                 count++;
148             }
149             return count;
150         }
151 
MoveToFirstChild()152         internal bool MoveToFirstChild() {
153             RealFoliate();
154             AssertValid();
155             if (node == null)
156                 return false;
157 
158             if (column != null) {
159                 if (fOnValue)
160                     return false;
161 
162                 fOnValue = true;
163                 return true;
164             }
165             else if (!IsFoliated( node )) {
166                 // find virtual column elements first
167                 DataColumn c = NextColumn( Row, null, false, false );
168                 if (c != null) {
169                     MoveTo( node, c, doc.IsTextOnly(c) );
170                     return true;
171                 }
172             }
173 
174             // look for anything
175             XmlNode n = doc.SafeFirstChild( node );
176             if (n != null) {
177                 MoveTo(n);
178                 return true;
179             }
180 
181             return false;
182         }
183 
MoveToNextSibling()184         internal bool MoveToNextSibling() {
185             RealFoliate();
186             AssertValid();
187             if (node != null) {
188                 if (column != null) {
189                     if (fOnValue && !doc.IsTextOnly(column))
190                         return false;
191 
192                     DataColumn c = NextColumn( Row, column, false, false );
193                     if (c != null) {
194                         MoveTo( this.node, c, false );
195                         return true;
196                     }
197 
198                     XmlNode n = doc.SafeFirstChild( node );
199                     if (n != null) {
200                         MoveTo( n );
201                         return true;
202                     }
203                 }
204                 else {
205                     XmlNode n = doc.SafeNextSibling( node );
206                     if (n != null) {
207                         MoveTo(n);
208                         return true;
209                     }
210                 }
211             }
212 
213             return false;
214         }
215 
MoveToParent()216         internal bool MoveToParent() {
217             RealFoliate();
218             AssertValid();
219             if (node != null) {
220                 if (column != null) {
221                     if (fOnValue && !doc.IsTextOnly(column)) {
222                         MoveTo( node, column, false );
223                         return true;
224                     }
225 
226                     if (column.ColumnMapping != MappingType.Attribute) {
227                         MoveTo( node, null, false );
228                         return true;
229                     }
230                 }
231                 else {
232                     XmlNode n = node.ParentNode;
233                     if (n != null) {
234                         MoveTo(n);
235                         return true;
236                     }
237                 }
238             }
239             return false;
240         }
241 
MoveToOwnerElement()242         internal bool MoveToOwnerElement() {
243             RealFoliate();
244             AssertValid();
245             if (node != null) {
246                 if (column != null) {
247                     if (fOnValue || doc.IsTextOnly(column) || column.ColumnMapping != MappingType.Attribute)
248                         return false;
249 
250                     MoveTo( node, null, false );
251                     return true;
252                 }
253                 else if (node.NodeType == XmlNodeType.Attribute) {
254                     XmlNode n = ((XmlAttribute)node).OwnerElement;
255                     if (n != null) {
256                         MoveTo( n, null, false );
257                         return true;
258                     }
259                 }
260             }
261 
262             return false;
263         }
264 
265 
266         internal int AttributeCount {
267             get {
268                 RealFoliate();
269                 AssertValid();
270                 if (node != null) {
271                     if (column == null && node.NodeType == XmlNodeType.Element) {
272                         if (!IsFoliated( node )) {
273                             return ColumnCount( Row, true, false );
274                         }
275                         else
276                             return node.Attributes.Count;
277                     }
278                 }
279                 return 0;
280             }
281         }
282 
MoveToAttribute( int i )283         internal bool MoveToAttribute( int i ) {
284             RealFoliate();
285             AssertValid();
286             if ( i < 0 )
287                 return false;
288             if (node != null) {
289                 if ((column == null || column.ColumnMapping == MappingType.Attribute) && node.NodeType == XmlNodeType.Element) {
290                     if (!IsFoliated( node )) {
291                         DataColumn c = NthColumn( Row, true, i, false );
292                         if (c != null) {
293                             MoveTo( node, c, false );
294                             return true;
295                         }
296                     }
297                     else {
298                         XmlNode n = node.Attributes.Item(i);
299                         if (n != null) {
300                             MoveTo( n, null, false );
301                             return true;
302                         }
303                     }
304                 }
305             }
306             return false;
307         }
308 
309         internal XmlNodeType NodeType {
310             get {
311                 RealFoliate();
312                 AssertValid();
313                 if (this.node == null) {
314                     return XmlNodeType.None;
315                 }
316                 else if (this.column == null) {
317                     return this.node.NodeType;
318                 }
319                 else if (this.fOnValue) {
320                     return XmlNodeType.Text;
321                 }
322                 else if (this.column.ColumnMapping == MappingType.Attribute) {
323                     return XmlNodeType.Attribute;
324                 }
325                 else {
326                     return XmlNodeType.Element;
327                 }
328             }
329         }
330 
331         internal string LocalName {
332             get {
333                 RealFoliate();
334                 AssertValid();
335                 if (this.node == null) {
336                     return string.Empty;
337                 }else if (this.column == null) {
338                     String name = node.LocalName;
339                     Debug.Assert( name != null );
340                     if ( IsLocalNameEmpty( this.node.NodeType ) )
341                         return String.Empty;
342                     return name;
343                 }
344                 else if (this.fOnValue) {
345                     return String.Empty;
346                 }
347                 else {
348                     return doc.NameTable.Add(column.EncodedColumnName);
349                 }
350             }
351         }
352 
353         internal string NamespaceURI {
354             get {
355                 RealFoliate();
356                 AssertValid();
357                 if (this.node == null) {
358                     return string.Empty;
359                 }
360                 else if (this.column == null) {
361                     return node.NamespaceURI;
362                 }
363                 else if (this.fOnValue) {
364                     return string.Empty;
365                 }
366                 else {
367                     return doc.NameTable.Add(column.Namespace);
368                 }
369             }
370         }
371 
372         internal string Name {
373             get {
374                 RealFoliate();
375                 AssertValid();
376                 if (this.node == null) {
377                     return string.Empty;
378                 }
379                 else if (this.column == null) {
380                     String name = node.Name;
381                     //Again it could be String.Empty at null position
382                     Debug.Assert( name != null );
383                     if ( IsLocalNameEmpty( this.node.NodeType ) )
384                         return String.Empty;
385                     return name;
386                 }
387                 else {
388                     string prefix = Prefix;
389                     string lname = LocalName;
390                     if (prefix != null && prefix.Length > 0) {
391                         if (lname != null && lname.Length > 0) {
392                             return doc.NameTable.Add( prefix + ":" + lname );
393                         }
394                         else {
395                             return prefix;
396                         }
397                     }
398                     else {
399                         return lname;
400                     }
401                 }
402             }
403         }
404 
405 
IsLocalNameEmpty( XmlNodeType nt)406         private bool IsLocalNameEmpty ( XmlNodeType nt) {
407                 switch ( nt ) {
408                     case XmlNodeType.None :
409                     case XmlNodeType.Text :
410                     case XmlNodeType.CDATA :
411                     case XmlNodeType.Comment :
412                     case XmlNodeType.Document :
413                     case XmlNodeType.DocumentFragment :
414                     case XmlNodeType.Whitespace :
415                     case XmlNodeType.SignificantWhitespace :
416                     case XmlNodeType.EndElement :
417                     case XmlNodeType.EndEntity :
418                         return true;
419                     case XmlNodeType.Element :
420                     case XmlNodeType.Attribute :
421                     case XmlNodeType.EntityReference :
422                     case XmlNodeType.Entity :
423                     case XmlNodeType.ProcessingInstruction :
424                     case XmlNodeType.DocumentType :
425                     case XmlNodeType.Notation :
426                     case XmlNodeType.XmlDeclaration :
427                         return false;
428                     default :
429                         return true;
430                 }
431             }
432 
433         internal string Prefix {
434             get {
435                 RealFoliate();
436                 AssertValid();
437                 if (this.node == null) {
438                     return string.Empty;
439                 }
440                 else if (this.column == null) {
441                     return node.Prefix;
442                 }
443                 else {
444                     return string.Empty;
445                 }
446             }
447         }
448 
449         internal string Value {
450             get {
451                 RealFoliate();
452                 AssertValid();
453                 if (this.node == null) {
454                     return null;
455                 }
456                 else if (this.column == null) {
457                     return this.node.Value;
458                 }
459                 else if (this.column.ColumnMapping == MappingType.Attribute || this.fOnValue) {
460                     DataRow row = this.Row;
461                     DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
462                     object value = row[ this.column, rowVersion ];
463                     if ( ! Convert.IsDBNull( value ) )
464                         return this.column.ConvertObjectToXml( value );
465                     return null;
466                 }
467                 else {
468                     // column element has no value
469                     return null;
470                 }
471             }
472         }
473 
IXmlDataVirtualNode.IsOnNode( XmlNode nodeToCheck )474         bool IXmlDataVirtualNode.IsOnNode( XmlNode nodeToCheck ) {
475             RealFoliate();
476             return nodeToCheck == this.node;
477         }
478 
IXmlDataVirtualNode.IsOnColumn( DataColumn col )479         bool IXmlDataVirtualNode.IsOnColumn( DataColumn col ) {
480             RealFoliate();
481             return col == this.column;
482         }
483 
GetNode()484         internal XmlNode GetNode() {
485             return this.node;
486         }
487 
488         internal bool IsEmptyElement {
489             get {
490                 RealFoliate();
491                 AssertValid();
492                 if (node != null && column == null) {
493                     //
494                     if (node.NodeType == XmlNodeType.Element) {
495                         return((XmlElement)node).IsEmpty;
496                     }
497                 }
498                 return false;
499             }
500         }
501 
502         internal bool IsDefault {
503             get {
504                 RealFoliate();
505                 AssertValid();
506                 if (node != null && column == null && node.NodeType == XmlNodeType.Attribute) {
507                     return !((XmlAttribute)node).Specified;
508                 }
509 
510                 return false;
511             }
512         }
513 
IXmlDataVirtualNode.OnFoliated( XmlNode foliatedNode )514         void IXmlDataVirtualNode.OnFoliated( XmlNode foliatedNode ) {
515             // update the pointer if the element node has been foliated
516             if (node == foliatedNode) {
517                 // if already on this node, nothing to do!
518                 if (column == null)
519                     return;
520                 bNeedFoliate = true;
521             }
522         }
523 
RealFoliate()524         internal void RealFoliate() {
525             if ( !bNeedFoliate )
526                 return;
527 
528             XmlNode n = null;
529 
530             if (doc.IsTextOnly( column )) {
531                 n = node.FirstChild;
532             }
533             else {
534                 if (column.ColumnMapping == MappingType.Attribute) {
535                     n = node.Attributes.GetNamedItem( column.EncodedColumnName, column.Namespace );
536                 }
537                 else {
538                     for (n = node.FirstChild; n != null; n = n.NextSibling) {
539                         if (n.LocalName == column.EncodedColumnName && n.NamespaceURI == column.Namespace)
540                             break;
541                     }
542                 }
543 
544                 if (n != null && fOnValue)
545                     n = n.FirstChild;
546             }
547 
548             if (n == null)
549                 throw new InvalidOperationException(Res.GetString(Res.DataDom_Foliation));
550 
551             // Cannot use MoveTo( n ); b/c the initial state for MoveTo is invalid (region is foliated but this is not)
552             this.node = n;
553             this.column = null;
554             this.fOnValue = false;
555             AssertValid();
556 
557             bNeedFoliate = false;
558         }
559 
560         //for the 6 properties below, only when the this.column == null that the nodetype could be XmlDeclaration node
561         internal String PublicId {
562             get {
563                 XmlNodeType nt = NodeType;
564                 switch ( nt ) {
565                     case XmlNodeType.DocumentType : {
566                         Debug.Assert( this.column == null );
567                         return ( ( XmlDocumentType ) (this.node)).PublicId;
568                     }
569                     case XmlNodeType.Entity : {
570                         Debug.Assert( this.column == null );
571                         return ( ( XmlEntity ) (this.node)).PublicId;
572                     }
573                     case XmlNodeType.Notation : {
574                         Debug.Assert( this.column == null );
575                         return ( ( XmlNotation ) (this.node)).PublicId;
576                     }
577                 }
578                 return null;
579             }
580         }
581 
582         internal String SystemId {
583             get {
584                 XmlNodeType nt = NodeType;
585                 switch ( nt ) {
586                     case XmlNodeType.DocumentType : {
587                         Debug.Assert( this.column == null );
588                         return ( ( XmlDocumentType ) (this.node)).SystemId;
589                     }
590                     case XmlNodeType.Entity : {
591                         Debug.Assert( this.column == null );
592                         return ( ( XmlEntity ) (this.node)).SystemId;
593                     }
594                     case XmlNodeType.Notation : {
595                         Debug.Assert( this.column == null );
596                         return ( ( XmlNotation ) (this.node)).SystemId;
597                     }
598                 }
599                 return null;
600             }
601         }
602 
603         internal String InternalSubset {
604             get {
605                 if ( NodeType == XmlNodeType.DocumentType ) {
606                     Debug.Assert( this.column == null );
607                     return ( ( XmlDocumentType ) (this.node)).InternalSubset;
608                 }
609                 return null;
610             }
611         }
612 
613         internal XmlDeclaration Declaration {
614             get {
615                 XmlNode child = doc.SafeFirstChild(doc);
616                 if ( child != null && child.NodeType == XmlNodeType.XmlDeclaration )
617                     return (XmlDeclaration)child;
618                 return null;
619             }
620         }
621 
622         internal String Encoding {
623             get {
624                 if ( NodeType == XmlNodeType.XmlDeclaration ) {
625                     Debug.Assert( this.column == null );
626                     return ( ( XmlDeclaration ) (this.node)).Encoding;
627                 } else if ( NodeType == XmlNodeType.Document ) {
628                     XmlDeclaration dec = Declaration;
629                     if ( dec != null )
630                         return dec.Encoding;
631                 }
632                 return null;
633             }
634         }
635 
636         internal String Standalone {
637             get {
638                 if ( NodeType == XmlNodeType.XmlDeclaration ) {
639                     Debug.Assert( this.column == null );
640                     return ( ( XmlDeclaration ) (this.node)).Standalone;
641                 } else if ( NodeType == XmlNodeType.Document ) {
642                     XmlDeclaration dec = Declaration;
643                     if ( dec != null )
644                         return dec.Standalone;
645                 }
646                 return null;
647             }
648         }
649 
650         internal String Version {
651             get {
652                 if ( NodeType == XmlNodeType.XmlDeclaration ) {
653                     Debug.Assert( this.column == null );
654                     return ( ( XmlDeclaration ) (this.node)).Version;
655                 } else if ( NodeType == XmlNodeType.Document ) {
656                     XmlDeclaration dec = Declaration;
657                     if ( dec != null )
658                         return dec.Version;
659                 }
660                 return null;
661             }
662         }
663 
664         [System.Diagnostics.Conditional("DEBUG")]
AssertValid()665         private void AssertValid() {
666             // This pointer must be int the document list
667             if ( this.column != null ) {
668                 // We must be on a de-foliated region
669                 XmlBoundElement rowElem = this.node as XmlBoundElement;
670                 Debug.Assert( rowElem != null );
671 
672                 DataRow row = rowElem.Row;
673                 Debug.Assert( row != null );
674 
675                 ElementState state = rowElem.ElementState;
676                 Debug.Assert( state == ElementState.Defoliated, "Region is accessed using column, but it's state is FOLIATED" );
677 
678                 // We cannot be on a column for which the value is DBNull
679                 DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
680                 Debug.Assert( ! Convert.IsDBNull( row[ this.column, rowVersion ] ) );
681 
682                 // If we are on the Text column, we should always have fOnValue == true
683                 Debug.Assert( (this.column.ColumnMapping == MappingType.SimpleContent) ? (this.fOnValue == true) : true );
684             }
685         }
686 
IXmlDataVirtualNode.IsInUse()687         bool IXmlDataVirtualNode.IsInUse() {
688             return _isInUse;
689         }
690 
SetNoLongerUse()691         internal void SetNoLongerUse() {
692             this.node = null;
693             this.column = null;
694             this.fOnValue = false;
695             this.bNeedFoliate = false;
696             this._isInUse = false;
697         }
698 
699     }
700 }
701