1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.Data;
9 using System.Diagnostics;
10 using System.IO;
11 using System.Xml.XPath;
12 
13 namespace System.Xml
14 {
15     /// <summary>
16     /// Represents an entire document. An XmlDataDocument can contain XML
17     /// data or relational data (DataSet).
18     /// </summary>
19     [Obsolete("XmlDataDocument class will be removed in a future release.")]
20 #if MONO // keep it public in MONO
21     public class XmlDataDocument : XmlDocument
22 #else
23     internal class XmlDataDocument : XmlDocument
24 #endif
25     {
26         private DataSet _dataSet;
27 
28         private DataSetMapper _mapper;
29         internal Hashtable _pointers;         // Hastable w/ all pointer objects used by this XmlDataDocument. Hashtable are guaranteed to work OK w/ one writer and mutiple readers, so as long as we guarantee
30                                               // that there is at most one thread in AddPointer we are OK.
31         private int _countAddPointer;    // Approximate count of how many times AddPointer was called since the last time we removed the unused pointer objects from pointers hashtable.
32         private ArrayList _columnChangeList;
33         private DataRowState _rollbackState;
34 
35         private bool _fBoundToDataSet;       // true if our permanent event listeners are registered to receive DataSet events
36         private bool _fBoundToDocument;      // true if our permanent event listeners are registered to receive XML events. Note that both fBoundToDataSet and fBoundToDataSet should be both true or false.
37         private bool _fDataRowCreatedSpecial;    // true if our special event listener is registered to receive DataRowCreated events. Note that we either have special listeners subsribed or permanent ones (i.e. fDataRowCreatedSpecial and fBoundToDocument/fBoundToDataSet cannot be both true).
38         private bool _ignoreXmlEvents;       // true if XML events should not be processed
39         private bool _ignoreDataSetEvents;   // true if DataSet events should not be processed
40         private bool _isFoliationEnabled;    // true if we should create and reveal the virtual nodes, false if we should reveal only the physical stored nodes
41         private bool _optimizeStorage;       // false if we should only have foilated regions.
42         private ElementState _autoFoliationState;    // When XmlBoundElement will foliate because of member functions, this will contain the foliation mode: usually this is
43                                                      // ElementState.StrongFoliation, however when foliation occurs due to DataDocumentNavigator operations (InsertNode for example),
44                                                      // it it usually ElementState.WeakFoliation
45         private bool _fAssociateDataRow;     // if true, CreateElement will create and associate data rows w/ the newly created XmlBoundElement.
46                                              // If false, then CreateElement will just create the XmlBoundElement nodes. This is usefull for Loading case,
47                                              // when CreateElement is called by DOM.
48         private object _foliationLock;
49         internal const string XSI_NIL = "xsi:nil";
50         internal const string XSI = "xsi";
51         private bool _bForceExpandEntity = false;
52         internal XmlAttribute _attrXml = null;
53         internal bool _bLoadFromDataSet = false;
54         internal bool _bHasXSINIL = false;
55 
AddPointer(IXmlDataVirtualNode pointer)56         internal void AddPointer(IXmlDataVirtualNode pointer)
57         {
58             Debug.Assert(_pointers.ContainsValue(pointer) == false);
59             lock (_pointers)
60             {
61                 _countAddPointer++;
62                 if (_countAddPointer >= 5)
63                 {   // 5 is choosed to be small enough to not affect perf, but high enough so we will not scan all the time
64                     ArrayList al = new ArrayList();
65                     foreach (DictionaryEntry entry in _pointers)
66                     {
67                         IXmlDataVirtualNode temp = (IXmlDataVirtualNode)(entry.Value);
68                         Debug.Assert(temp != null);
69                         if (!temp.IsInUse())
70                             al.Add(temp);
71                     }
72                     for (int i = 0; i < al.Count; i++)
73                     {
74                         _pointers.Remove(al[i]);
75                     }
76                     _countAddPointer = 0;
77                 }
78                 _pointers[pointer] = pointer;
79             }
80         }
81 
82         [System.Diagnostics.Conditional("DEBUG")]
AssertPointerPresent(IXmlDataVirtualNode pointer)83         internal void AssertPointerPresent(IXmlDataVirtualNode pointer)
84         {
85 #if DEBUG
86             object val = _pointers[pointer];
87             if (val != (object)pointer)
88                 Debug.Assert(false);
89 #endif
90         }
91         // This function attaches the DataSet to XmlDataDocument
92         // We also register a special listener (OnDataRowCreatedSpecial) to DataSet, so we know when we should setup all regular listeners (OnDataRowCreated, OnColumnChanging, etc).
93         // We need to do this because of the following scenario:
94         //  - XmlDataDocument doc = new XmlDataDocument();
95         //  - DataSet ds = doc.DataSet;     // doc.DataSet creates a data-set, however does not sets-up the regular listeners.
96         //  - ds.ReadXmlSchema();           // since there are regular listeners in doc that track ds schema changes, doc does not know about the new tables/columns/etc
97         //  - ds.ReadXmlData();             // ds is now filled, however doc has no content (since there were no listeners for the new created DataRow's)
98         // We can set-up listeners and track each change in schema, but it is more perf-friendly to do it laizily, all at once, when the first DataRow is created
99         // (we rely on the fact that DataRowCreated is a DataSet wide event, rather than a DataTable event)
AttachDataSet(DataSet ds)100         private void AttachDataSet(DataSet ds)
101         {
102             // You should not have already an associated dataset
103             Debug.Assert(_dataSet == null);
104             Debug.Assert(ds != null);
105             if (ds.FBoundToDocument)
106                 throw new ArgumentException(SR.DataDom_MultipleDataSet);
107             ds.FBoundToDocument = true;
108             _dataSet = ds;
109             // Register the special listener to catch the first DataRow event(s)
110             BindSpecialListeners();
111         }
112 
113         // after loading, all detached DataRows are synchronized with the xml tree and inserted to their tables
114         // or after setting the innerxml, synchronize the rows and if created new and detached, will be inserted.
SyncRows(DataRow parentRow, XmlNode node, bool fAddRowsToTable)115         internal void SyncRows(DataRow parentRow, XmlNode node, bool fAddRowsToTable)
116         {
117             XmlBoundElement be = node as XmlBoundElement;
118             if (be != null)
119             {
120                 DataRow r = be.Row;
121                 if (r != null && be.ElementState == ElementState.Defoliated)
122                     return; //no need of syncRow
123 
124                 if (r != null)
125                 {
126                     // get all field values.
127                     SynchronizeRowFromRowElement(be);
128 
129                     // defoliate if possible
130                     be.ElementState = ElementState.WeakFoliation;
131                     DefoliateRegion(be);
132 
133                     if (parentRow != null)
134                         SetNestedParentRow(r, parentRow);
135                     if (fAddRowsToTable && r.RowState == DataRowState.Detached)
136                         r.Table.Rows.Add(r);
137                     parentRow = r;
138                 }
139             }
140 
141             // Attach all rows from children nodes
142             for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
143                 SyncRows(parentRow, child, fAddRowsToTable);
144         }
145 
146         // All detached DataRows are synchronized with the xml tree and inserted to their tables.
147         // Synchronize the rows and if created new and detached, will be inserted.
SyncTree(XmlNode node)148         internal void SyncTree(XmlNode node)
149         {
150             XmlBoundElement be = null;
151             _mapper.GetRegion(node, out be);
152             DataRow parentRow = null;
153             bool fAddRowsToTable = IsConnected(node);
154 
155             if (be != null)
156             {
157                 DataRow r = be.Row;
158                 if (r != null && be.ElementState == ElementState.Defoliated)
159                     return; //no need of syncRow
160 
161                 if (r != null)
162                 {
163                     // get all field values.
164                     SynchronizeRowFromRowElement(be);
165 
166                     // defoliation will not be done on the node which is not RowElement, in case of node is externally being used
167                     if (node == be)
168                     {
169                         // defoliate if possible
170                         be.ElementState = ElementState.WeakFoliation;
171                         DefoliateRegion(be);
172                     }
173 
174                     if (fAddRowsToTable && r.RowState == DataRowState.Detached)
175                         r.Table.Rows.Add(r);
176 
177                     parentRow = r;
178                 }
179             }
180 
181             // Attach all rows from children nodes
182             for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
183                 SyncRows(parentRow, child, fAddRowsToTable);
184         }
185 
186         internal ElementState AutoFoliationState
187         {
188             get { return _autoFoliationState; }
189             set { _autoFoliationState = value; }
190         }
191 
BindForLoad()192         private void BindForLoad()
193         {
194             Debug.Assert(_ignoreXmlEvents == true);
195             _ignoreDataSetEvents = true;
196             _mapper.SetupMapping(this, _dataSet);
197             if (_dataSet.Tables.Count > 0)
198             {
199                 //at least one table
200                 LoadDataSetFromTree();
201             }
202             BindListeners();
203             _ignoreDataSetEvents = false;
204         }
205 
Bind(bool fLoadFromDataSet)206         private void Bind(bool fLoadFromDataSet)
207         {
208             // If we have a DocumentElement then it is illegal to call this func to load from data-set
209             Debug.Assert(DocumentElement == null || !fLoadFromDataSet);
210 
211             _ignoreDataSetEvents = true;
212             _ignoreXmlEvents = true;
213 
214             // Do the mapping. This could be a successive mapping in case of this scenario: xd = XmlDataDocument( emptyDataSet ); xd.Load( "file.xml" );
215             _mapper.SetupMapping(this, _dataSet);
216 
217             if (DocumentElement != null)
218             {
219                 LoadDataSetFromTree();
220                 BindListeners();
221             }
222             else if (fLoadFromDataSet)
223             {
224                 _bLoadFromDataSet = true;
225                 LoadTreeFromDataSet(DataSet);
226                 BindListeners();
227             }
228 
229             _ignoreDataSetEvents = false;
230             _ignoreXmlEvents = false;
231         }
232 
Bind(DataRow r, XmlBoundElement e)233         internal void Bind(DataRow r, XmlBoundElement e)
234         {
235             r.Element = e;
236             e.Row = r;
237         }
238 
239         // Binds special listeners to catch the 1st data-row created. When the 1st DataRow is created, XmlDataDocument will automatically bind all regular listeners.
BindSpecialListeners()240         private void BindSpecialListeners()
241         {
242             Debug.Assert(_fDataRowCreatedSpecial == false);
243             Debug.Assert(_fBoundToDataSet == false && _fBoundToDocument == false);
244             _dataSet.DataRowCreated += new DataRowCreatedEventHandler(OnDataRowCreatedSpecial);
245             _fDataRowCreatedSpecial = true;
246         }
UnBindSpecialListeners()247         private void UnBindSpecialListeners()
248         {
249             Debug.Assert(_fDataRowCreatedSpecial == true);
250             _dataSet.DataRowCreated -= new DataRowCreatedEventHandler(OnDataRowCreatedSpecial);
251             _fDataRowCreatedSpecial = false;
252         }
253 
BindListeners()254         private void BindListeners()
255         {
256             BindToDocument();
257             BindToDataSet();
258         }
259 
BindToDataSet()260         private void BindToDataSet()
261         {
262             // We could be already bound to DataSet in this scenario:
263             //     xd = new XmlDataDocument( dataSetThatHasNoData ); xd.Load( "foo.xml" );
264             // so we must not rebound again to it.
265             if (_fBoundToDataSet)
266             {
267                 Debug.Assert(_dataSet != null);
268                 return;
269             }
270 
271             // Unregister the DataRowCreatedSpecial notification
272             if (_fDataRowCreatedSpecial)
273                 UnBindSpecialListeners();
274 
275             _dataSet.Tables.CollectionChanging += new CollectionChangeEventHandler(OnDataSetTablesChanging);
276             _dataSet.Relations.CollectionChanging += new CollectionChangeEventHandler(OnDataSetRelationsChanging);
277             _dataSet.DataRowCreated += new DataRowCreatedEventHandler(OnDataRowCreated);
278             _dataSet.PropertyChanging += new PropertyChangedEventHandler(OnDataSetPropertyChanging);
279 
280             //this is the hack for this release, should change it in the future
281             _dataSet.ClearFunctionCalled += new DataSetClearEventhandler(OnClearCalled);
282 
283             if (_dataSet.Tables.Count > 0)
284             {
285                 foreach (DataTable t in _dataSet.Tables)
286                 {
287                     BindToTable(t);
288                 }
289             }
290 
291             foreach (DataRelation rel in _dataSet.Relations)
292             {
293                 rel.PropertyChanging += new PropertyChangedEventHandler(OnRelationPropertyChanging);
294             }
295             _fBoundToDataSet = true;
296         }
297 
BindToDocument()298         private void BindToDocument()
299         {
300             if (!_fBoundToDocument)
301             {
302                 NodeInserting += new XmlNodeChangedEventHandler(OnNodeInserting);
303                 NodeInserted += new XmlNodeChangedEventHandler(OnNodeInserted);
304                 NodeRemoving += new XmlNodeChangedEventHandler(OnNodeRemoving);
305                 NodeRemoved += new XmlNodeChangedEventHandler(OnNodeRemoved);
306                 NodeChanging += new XmlNodeChangedEventHandler(OnNodeChanging);
307                 NodeChanged += new XmlNodeChangedEventHandler(OnNodeChanged);
308                 _fBoundToDocument = true;
309             }
310         }
311 
BindToTable(DataTable t)312         private void BindToTable(DataTable t)
313         {
314             t.ColumnChanged += new DataColumnChangeEventHandler(OnColumnChanged);
315             t.RowChanging += new DataRowChangeEventHandler(OnRowChanging);
316             t.RowChanged += new DataRowChangeEventHandler(OnRowChanged);
317             t.RowDeleting += new DataRowChangeEventHandler(OnRowChanging);
318             t.RowDeleted += new DataRowChangeEventHandler(OnRowChanged);
319             t.PropertyChanging += new PropertyChangedEventHandler(OnTablePropertyChanging);
320             t.Columns.CollectionChanging += new CollectionChangeEventHandler(OnTableColumnsChanging);
321 
322             foreach (DataColumn col in t.Columns)
323             {
324                 // Hook column properties changes, so we can react properly to ROM changes.
325                 col.PropertyChanging += new PropertyChangedEventHandler(OnColumnPropertyChanging);
326             }
327         }
328 
329         /// <summary>
330         /// Creates an element with the specified Prefix, LocalName, and
331         /// NamespaceURI.
332         /// </summary>
CreateElement(string prefix, string localName, string namespaceURI)333         public override XmlElement CreateElement(string prefix, string localName, string namespaceURI)
334         {
335             // There are three states for the document:
336             //  - special listeners ON, no permananent listeners: this is when the data doc was created w/o any dataset, and the 1st time a new row/element
337             //    is created we should subscribe the permenent listeners.
338             //  - special listeners OFF, permanent listeners ON: this is when the data doc is loaded (from dataset or XML file) and synchronization takes place.
339             //  - special listeners OFF, permanent listeners OFF: this is then the data doc is LOADING (from dataset or XML file) - the synchronization is done by code,
340             //    not based on listening to events.
341 #if DEBUG
342             // Cannot have both special and permananent listeners ON
343             if (_fDataRowCreatedSpecial)
344                 Debug.Assert((_fBoundToDataSet == false) && (_fBoundToDocument == false));
345             // fBoundToDataSet and fBoundToDocument should have the same value
346             Debug.Assert(_fBoundToDataSet ? _fBoundToDocument : (!_fBoundToDocument));
347 #endif
348             if (prefix == null)
349                 prefix = string.Empty;
350             if (namespaceURI == null)
351                 namespaceURI = string.Empty;
352 
353             if (!_fAssociateDataRow)
354             {
355                 // Loading state: create just the XmlBoundElement: the LoadTreeFromDataSet/LoadDataSetFromTree will take care of synchronization
356                 return new XmlBoundElement(prefix, localName, namespaceURI, this);
357             }
358 
359             // This is the 1st time an element is beeing created on an empty XmlDataDocument - unbind special listeners, bind permanent ones and then go on w/
360             // creation of this element
361             EnsurePopulatedMode();
362             Debug.Assert(_fDataRowCreatedSpecial == false);
363 
364             // Loaded state: create a DataRow, this in turn will create and associate the XmlBoundElement, which we will return.
365             DataTable dt = _mapper.SearchMatchingTableSchema(localName, namespaceURI);
366             if (dt != null)
367             {
368                 DataRow row = dt.CreateEmptyRow();
369                 // We need to make sure all fields are DBNull
370                 foreach (DataColumn col in dt.Columns)
371                 {
372                     if (col.ColumnMapping != MappingType.Hidden)
373                         SetRowValueToNull(row, col);
374                 }
375                 XmlBoundElement be = row.Element;
376                 Debug.Assert(be != null);
377                 be.Prefix = prefix;
378                 return be;
379             }
380             // No matching table schema for this element: just create the element
381             return new XmlBoundElement(prefix, localName, namespaceURI, this);
382         }
383 
CreateEntityReference(string name)384         public override XmlEntityReference CreateEntityReference(string name)
385         {
386             throw new NotSupportedException(SR.DataDom_NotSupport_EntRef);
387         }
388 
389         /// <summary>
390         /// Gets a DataSet that provides a relational representation of the data in this
391         /// XmlDataDocument.
392         /// </summary>
393         public DataSet DataSet
394         {
395             get
396             {
397                 return _dataSet;
398             }
399         }
400 
DefoliateRegion(XmlBoundElement rowElem)401         private void DefoliateRegion(XmlBoundElement rowElem)
402         {
403             // You must pass a row element (which s/b associated w/ a DataRow)
404             Debug.Assert(rowElem.Row != null);
405 
406             if (!_optimizeStorage)
407                 return;
408 
409             if (rowElem.ElementState != ElementState.WeakFoliation)
410                 return;
411 
412             if (!_mapper.IsRegionRadical(rowElem))
413             {
414                 return;
415             }
416 
417             bool saveIgnore = IgnoreXmlEvents;
418             IgnoreXmlEvents = true;
419 
420             rowElem.ElementState = ElementState.Defoliating;
421 
422             try
423             {
424                 // drop all attributes
425                 rowElem.RemoveAllAttributes();
426 
427                 XmlNode node = rowElem.FirstChild;
428                 while (node != null)
429                 {
430                     XmlNode next = node.NextSibling;
431 
432                     XmlBoundElement be = node as XmlBoundElement;
433                     if (be != null && be.Row != null)
434                         break;
435 
436                     // The node must be mapped to a column (since the region is radically structured)
437                     Debug.Assert(_mapper.GetColumnSchemaForNode(rowElem, node) != null);
438                     rowElem.RemoveChild(node);
439 
440                     node = next;
441                 }
442 #if DEBUG
443                 // All subsequent siblings must be sub-regions
444                 for (; node != null; node = node.NextSibling)
445                 {
446                     Debug.Assert((node is XmlBoundElement) && (((XmlBoundElement)node).Row != null));
447                 }
448 #endif
449 
450                 rowElem.ElementState = ElementState.Defoliated;
451             }
452             finally
453             {
454                 IgnoreXmlEvents = saveIgnore;
455             }
456         }
457 
EnsureDocumentElement()458         private XmlElement EnsureDocumentElement()
459         {
460             XmlElement docelem = DocumentElement;
461             if (docelem == null)
462             {
463                 string docElemName = XmlConvert.EncodeLocalName(DataSet.DataSetName);
464                 if (docElemName == null || docElemName.Length == 0)
465                     docElemName = "Xml";
466                 string ns = DataSet.Namespace;
467                 if (ns == null)
468                     ns = string.Empty;
469                 docelem = new XmlBoundElement(string.Empty, docElemName, ns, this);
470                 AppendChild(docelem);
471             }
472 
473             return docelem;
474         }
EnsureNonRowDocumentElement()475         private XmlElement EnsureNonRowDocumentElement()
476         {
477             XmlElement docElem = DocumentElement;
478             if (docElem == null)
479                 return EnsureDocumentElement();
480 
481             DataRow rowDocElem = GetRowFromElement(docElem);
482             if (rowDocElem == null)
483                 return docElem;
484 
485             return DemoteDocumentElement();
486         }
DemoteDocumentElement()487         private XmlElement DemoteDocumentElement()
488         {
489             // Changes of Xml here should not affect ROM
490             Debug.Assert(_ignoreXmlEvents == true);
491             // There should be no reason to call this function if docElem is not a rowElem
492             Debug.Assert(GetRowFromElement(DocumentElement) != null);
493 
494             // Remove the DocumentElement and create a new one
495             XmlElement oldDocElem = DocumentElement;
496             RemoveChild(oldDocElem);
497             XmlElement docElem = EnsureDocumentElement();
498             docElem.AppendChild(oldDocElem);
499             // We should have only one child now
500             Debug.Assert(docElem.LastChild == docElem.FirstChild);
501             return docElem;
502         }
503         // This function ensures that the special listeners are un-subscribed, the permanent listeners are subscribed and
504         // CreateElement will attach DataRows to newly created XmlBoundElement.
505         // It should be called when we have special listeners hooked and we need to change from the special-listeners mode to the
506         // populated/permanenet mode where all listeners are correctly hooked up and the mapper is correctly set-up.
EnsurePopulatedMode()507         private void EnsurePopulatedMode()
508         {
509             // Unbind special listeners, bind permanent ones, setup the mapping, etc
510 #if DEBUG
511             bool fDataRowCreatedSpecialOld = _fDataRowCreatedSpecial;
512             bool fAssociateDataRowOld = _fAssociateDataRow;
513 #endif
514             if (_fDataRowCreatedSpecial)
515             {
516                 UnBindSpecialListeners();
517                 // If a special listener was ON, we should not have had an already set-up mapper or permanent listeners subscribed
518                 Debug.Assert(!_mapper.IsMapped());
519                 Debug.Assert(!_fBoundToDocument);
520                 Debug.Assert(!_fBoundToDataSet);
521 
522                 _mapper.SetupMapping(this, _dataSet);
523                 BindListeners();
524 
525                 // CreateElement should now create associate DataRows w/ new XmlBoundElement nodes
526                 // We should do this ONLY if we switch from special listeners to permanent listeners. The reason is
527                 // that DataDocumentNavigator wants to put XmlDataDocument in a batch mode, where CreateElement will just
528                 // create a XmlBoundElement (see DataDocumentNavigator.CloneTree)
529                 _fAssociateDataRow = true;
530             }
531 
532             Debug.Assert(_fDataRowCreatedSpecial == false);
533             Debug.Assert(_mapper.IsMapped());
534             Debug.Assert(_fBoundToDataSet && _fBoundToDocument);
535 #if DEBUG
536             // In case we EnsurePopulatedMode was called on an already populated mode, we should NOT change fAssociateDataRow
537             if (fDataRowCreatedSpecialOld == false)
538                 Debug.Assert(fAssociateDataRowOld == _fAssociateDataRow);
539 #endif
540         }
541 
542         // Move regions that are marked in ROM as nested children of row/rowElement as last children in XML fragment
FixNestedChildren(DataRow row, XmlElement rowElement)543         private void FixNestedChildren(DataRow row, XmlElement rowElement)
544         {
545             foreach (DataRelation dr in GetNestedChildRelations(row))
546             {
547                 foreach (DataRow r in row.GetChildRows(dr))
548                 {
549                     XmlElement childElem = r.Element;
550                     // childElem can be null when we create XML from DataSet (XmlDataDocument( DataSet ) is called) and we insert rowElem of the parentRow before
551                     // we insert the rowElem of children rows.
552                     if (childElem != null)
553                     {
554 #if DEBUG
555                         bool fIsChildConnected = IsConnected(childElem);
556 #endif
557                         if (childElem.ParentNode != rowElement)
558                         {
559                             childElem.ParentNode.RemoveChild(childElem);
560                             rowElement.AppendChild(childElem);
561                         }
562 #if DEBUG
563                         // We should not have changed the connected/disconnected state of the node (since the row state did not change)
564                         Debug.Assert(fIsChildConnected == IsConnected(childElem));
565                         Debug.Assert(IsRowLive(r) ? IsConnected(childElem) : !IsConnected(childElem));
566 #endif
567                     }
568                 }
569             }
570         }
571 
572         // This function accepts node params that are not row-elements. In this case, calling this function is a no-op
Foliate(XmlBoundElement node, ElementState newState)573         internal void Foliate(XmlBoundElement node, ElementState newState)
574         {
575             Debug.Assert(newState == ElementState.WeakFoliation || newState == ElementState.StrongFoliation);
576 #if DEBUG
577             // If we want to strong foliate one of the non-row-elem in a region, then the region MUST be strong-foliated (or there must be no region)
578             // Do this only when we are not loading
579             if (IsFoliationEnabled)
580             {
581                 if (newState == ElementState.StrongFoliation && node.Row == null)
582                 {
583                     XmlBoundElement rowElem;
584                     ElementState rowElemState = ElementState.None;
585                     if (_mapper.GetRegion(node, out rowElem))
586                     {
587                         rowElemState = rowElem.ElementState;
588                         Debug.Assert(rowElemState == ElementState.StrongFoliation || rowElemState == ElementState.WeakFoliation);
589                     }
590                     // Add a no-op, so we can still debug in the assert fails
591 
592 #pragma warning disable 1717 // assignment to self
593                     rowElemState = rowElemState;
594 #pragma warning restore 1717
595                 }
596             }
597 #endif
598 
599             if (IsFoliationEnabled)
600             {
601                 if (node.ElementState == ElementState.Defoliated)
602                 {
603                     ForceFoliation(node, newState);
604                 }
605                 else if (node.ElementState == ElementState.WeakFoliation && newState == ElementState.StrongFoliation)
606                 {
607                     // Node must be a row-elem
608                     Debug.Assert(node.Row != null);
609                     node.ElementState = newState;
610                 }
611             }
612         }
613 
Foliate(XmlElement element)614         private void Foliate(XmlElement element)
615         {
616             if (element is XmlBoundElement)
617                 ((XmlBoundElement)element).Foliate(ElementState.WeakFoliation);
618         }
619 
620         // Foliate rowElement region if there are DataPointers that points into it
FoliateIfDataPointers(DataRow row, XmlElement rowElement)621         private void FoliateIfDataPointers(DataRow row, XmlElement rowElement)
622         {
623             if (!IsFoliated(rowElement) && HasPointers(rowElement))
624             {
625                 bool wasFoliationEnabled = IsFoliationEnabled;
626                 IsFoliationEnabled = true;
627                 try
628                 {
629                     Foliate(rowElement);
630                 }
631                 finally
632                 {
633                     IsFoliationEnabled = wasFoliationEnabled;
634                 }
635             }
636         }
637 
EnsureFoliation(XmlBoundElement rowElem, ElementState foliation)638         private void EnsureFoliation(XmlBoundElement rowElem, ElementState foliation)
639         {
640             if (rowElem.IsFoliated) //perf reason, avoid unecessary lock.
641                 return;
642             ForceFoliation(rowElem, foliation);
643         }
644 
ForceFoliation(XmlBoundElement node, ElementState newState)645         private void ForceFoliation(XmlBoundElement node, ElementState newState)
646         {
647             lock (_foliationLock)
648             {
649                 if (node.ElementState != ElementState.Defoliated)
650                     // The region was foliated by an other thread while this thread was locked
651                     return;
652 
653                 // Node must be a row-elem associated w/ a non-deleted row
654                 Debug.Assert(node.Row != null);
655                 Debug.Assert(node.Row.RowState != DataRowState.Deleted);
656 
657                 node.ElementState = ElementState.Foliating;
658 
659                 bool saveIgnore = IgnoreXmlEvents;
660                 IgnoreXmlEvents = true;
661 
662                 try
663                 {
664                     XmlNode priorNode = null;
665                     DataRow row = node.Row;
666 
667                     // create new attrs & elements for row
668                     // For detached rows: we are in sync w/ temp values
669                     // For non-detached rows: we are in sync w/ the current values
670                     // For deleted rows: we never sync
671                     DataRowVersion rowVersion = (row.RowState == DataRowState.Detached) ? DataRowVersion.Proposed : DataRowVersion.Current;
672                     foreach (DataColumn col in row.Table.Columns)
673                     {
674                         if (!IsNotMapped(col))
675                         {
676                             object value = row[col, rowVersion];
677 
678                             if (!Convert.IsDBNull(value))
679                             {
680                                 if (col.ColumnMapping == MappingType.Attribute)
681                                 {
682                                     node.SetAttribute(col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml(value));
683                                 }
684                                 else
685                                 {
686                                     XmlNode newNode = null;
687                                     if (col.ColumnMapping == MappingType.Element)
688                                     {
689                                         newNode = new XmlBoundElement(string.Empty, col.EncodedColumnName, col.Namespace, this);
690                                         newNode.AppendChild(CreateTextNode(col.ConvertObjectToXml(value)));
691                                         if (priorNode != null)
692                                         {
693                                             node.InsertAfter(newNode, priorNode);
694                                         }
695                                         else if (node.FirstChild != null)
696                                         {
697                                             node.InsertBefore(newNode, node.FirstChild);
698                                         }
699                                         else
700                                         {
701                                             node.AppendChild(newNode);
702                                         }
703                                         priorNode = newNode;
704                                     }
705                                     else
706                                     {
707                                         Debug.Assert(col.ColumnMapping == MappingType.SimpleContent);
708                                         newNode = CreateTextNode(col.ConvertObjectToXml(value));
709                                         if (node.FirstChild != null)
710                                             node.InsertBefore(newNode, node.FirstChild);
711                                         else
712                                             node.AppendChild(newNode);
713                                         if (priorNode == null)
714                                             priorNode = newNode;
715                                     }
716                                 }
717                             }
718                             else
719                             {
720                                 if (col.ColumnMapping == MappingType.SimpleContent)
721                                 {
722                                     XmlAttribute attr = CreateAttribute(XSI, Keywords.XSI_NIL, Keywords.XSINS);
723                                     attr.Value = Keywords.TRUE;
724                                     node.SetAttributeNode(attr);
725                                     _bHasXSINIL = true;
726                                 }
727                             }
728                         }
729                     }
730                 }
731                 finally
732                 {
733                     IgnoreXmlEvents = saveIgnore;
734                     node.ElementState = newState;
735                 }
736                 // update all live pointers
737                 OnFoliated(node);
738             }
739         }
740 
741         //Determine best radical insert position for inserting column elements
GetColumnInsertAfterLocation(DataRow row, DataColumn col, XmlBoundElement rowElement)742         private XmlNode GetColumnInsertAfterLocation(DataRow row, DataColumn col, XmlBoundElement rowElement)
743         {
744             XmlNode prev = null;
745             XmlNode node = null;
746 
747             // text only columns appear first
748             if (IsTextOnly(col))
749                 return null;
750 
751             // insert location must be after free text
752             for (node = rowElement.FirstChild; node != null; prev = node, node = node.NextSibling)
753             {
754                 if (!IsTextLikeNode(node))
755                     break;
756             }
757 
758             for (; node != null; prev = node, node = node.NextSibling)
759             {
760                 // insert location must be before any non-element nodes
761                 if (node.NodeType != XmlNodeType.Element)
762                     break;
763                 XmlElement e = node as XmlElement;
764 
765                 // insert location must be before any non-mapped elements or separate regions
766                 if (_mapper.GetRowFromElement(e) != null)
767                     break;
768 
769                 object schema = _mapper.GetColumnSchemaForNode(rowElement, node);
770                 if (schema == null || !(schema is DataColumn))
771                     break;
772 
773                 // insert location must be before any columns logically after this column
774                 if (((DataColumn)schema).Ordinal > col.Ordinal)
775                     break;
776             }
777 
778             return prev;
779         }
780 
GetNestedChildRelations(DataRow row)781         private ArrayList GetNestedChildRelations(DataRow row)
782         {
783             ArrayList list = new ArrayList();
784 
785             foreach (DataRelation r in row.Table.ChildRelations)
786             {
787                 if (r.Nested)
788                     list.Add(r);
789             }
790 
791             return list;
792         }
793 
GetNestedParent(DataRow row)794         private DataRow GetNestedParent(DataRow row)
795         {
796             DataRelation relation = GetNestedParentRelation(row);
797             if (relation != null)
798                 return row.GetParentRow(relation);
799             return null;
800         }
801 
GetNestedParentRelation(DataRow row)802         private static DataRelation GetNestedParentRelation(DataRow row)
803         {
804             DataRelation[] relations = row.Table.NestedParentRelations;
805             if (relations.Length == 0)
806                 return null;
807             return relations[0];
808         }
809 
GetTextOnlyColumn(DataRow row)810         private DataColumn GetTextOnlyColumn(DataRow row)
811         {
812 #if DEBUG
813             {
814                 // Make sure there is at most only one text column, and the text column (if present) is the one reported by row.Table.XmlText
815                 DataColumnCollection columns = row.Table.Columns;
816                 int cCols = columns.Count;
817                 int cTextCols = 0;
818                 for (int iCol = 0; iCol < cCols; iCol++)
819                 {
820                     DataColumn c = columns[iCol];
821                     if (IsTextOnly(c))
822                     {
823                         Debug.Assert(c == row.Table.XmlText);
824                         ++cTextCols;
825                     }
826                 }
827                 Debug.Assert(cTextCols == 0 || cTextCols == 1);
828                 if (cTextCols == 0)
829                     Debug.Assert(row.Table.XmlText == null);
830             }
831 #endif
832             return row.Table.XmlText;
833         }
834 
835         /// <summary>
836         /// Retrieves the DataRow associated with the specified XmlElement.
837         /// </summary>
GetRowFromElement(XmlElement e)838         public DataRow GetRowFromElement(XmlElement e)
839         {
840             return _mapper.GetRowFromElement(e);
841         }
842 
GetRowInsertBeforeLocation(DataRow row, XmlElement rowElement, XmlNode parentElement)843         private XmlNode GetRowInsertBeforeLocation(DataRow row, XmlElement rowElement, XmlNode parentElement)
844         {
845             DataRow refRow = row;
846             int i = 0;
847             int pos;
848 
849             // Find position
850             // int pos = row.Table.Rows[row];
851             for (i = 0; i < row.Table.Rows.Count; i++)
852                 if (row == row.Table.Rows[i])
853                     break;
854             pos = i;
855 
856             DataRow parentRow = GetNestedParent(row);
857             for (i = pos + 1; i < row.Table.Rows.Count; i++)
858             {
859                 refRow = row.Table.Rows[i];
860                 if (GetNestedParent(refRow) == parentRow && GetElementFromRow(refRow).ParentNode == parentElement)
861                     break;
862             }
863 
864             if (i < row.Table.Rows.Count)
865                 return GetElementFromRow(refRow);
866             else
867                 return null;
868         }
869 
870 
871         /// <summary>
872         /// Retrieves the XmlElement associated with the specified DataRow.
873         /// </summary>
GetElementFromRow(DataRow r)874         public XmlElement GetElementFromRow(DataRow r)
875         {
876             XmlBoundElement be = r.Element;
877             Debug.Assert(be != null);
878             return be;
879         }
880 
HasPointers(XmlNode node)881         internal bool HasPointers(XmlNode node)
882         {
883             while (true)
884             {
885                 try
886                 {
887                     if (_pointers.Count > 0)
888                     {
889                         object pointer = null;
890                         foreach (DictionaryEntry entry in _pointers)
891                         {
892                             pointer = entry.Value;
893                             Debug.Assert(pointer != null);
894                             if (((IXmlDataVirtualNode)pointer).IsOnNode(node))
895                                 return true;
896                         }
897                     }
898                     return false;
899                 }
900                 catch (Exception e) when (Data.Common.ADP.IsCatchableExceptionType(e))
901                 {
902                     // This can happens only when some threads are creating navigators (thus modifying this.pointers) while other threads are in the foreach loop.
903                 }
904             }
905             //should never get to this point due to while (true) loop
906         }
907 
908         internal bool IgnoreXmlEvents
909         {
910             get { return _ignoreXmlEvents; }
911             set { _ignoreXmlEvents = value; }
912         }
913 
914         internal bool IgnoreDataSetEvents
915         {
916             get { return _ignoreDataSetEvents; }
917             set { _ignoreDataSetEvents = value; }
918         }
919 
IsFoliated(XmlElement element)920         private bool IsFoliated(XmlElement element)
921         {
922             if (element is XmlBoundElement)
923             {
924                 return ((XmlBoundElement)element).IsFoliated;
925             }
926 
927             return true;
928         }
IsFoliated(XmlBoundElement be)929         private bool IsFoliated(XmlBoundElement be)
930         {
931             return be.IsFoliated;
932         }
933 
934         internal bool IsFoliationEnabled
935         {
936             get { return _isFoliationEnabled; }
937             set { _isFoliationEnabled = value; }
938         }
939 
940         // This creates a tree and synchronize ROM w/ the created tree.
941         // It requires the populated mode to be on - in case we are not in populated mode, it will make the XmlDataDocument be in populated mode.
942         // It takes advantage of the fAssociateDataRow flag for populated mode, which allows creation of XmlBoundElement w/o associating DataRow objects.
CloneTree(DataPointer other)943         internal XmlNode CloneTree(DataPointer other)
944         {
945             EnsurePopulatedMode();
946 
947             bool oldIgnoreDataSetEvents = _ignoreDataSetEvents;
948             bool oldIgnoreXmlEvents = _ignoreXmlEvents;
949             bool oldFoliationEnabled = IsFoliationEnabled;
950             bool oldAssociateDataRow = _fAssociateDataRow;
951 
952             // Caller should ensure that the EnforceConstraints == false. See 60486 for more info about why this was changed from DataSet.EnforceConstraints = false to an assert.
953             Debug.Assert(DataSet.EnforceConstraints == false);
954             XmlNode newNode;
955 
956             try
957             {
958                 _ignoreDataSetEvents = true;
959                 _ignoreXmlEvents = true;
960                 IsFoliationEnabled = false;
961                 _fAssociateDataRow = false;
962 
963                 // Create the diconnected tree based on the other navigator
964                 newNode = CloneTreeInternal(other);
965                 Debug.Assert(newNode != null);
966 
967                 // Synchronize DataSet from XML
968                 LoadRows(null, newNode);
969                 SyncRows(null, newNode, false);
970             }
971             finally
972             {
973                 _ignoreDataSetEvents = oldIgnoreDataSetEvents;
974                 _ignoreXmlEvents = oldIgnoreXmlEvents;
975                 IsFoliationEnabled = oldFoliationEnabled;
976                 _fAssociateDataRow = oldAssociateDataRow;
977             }
978             return newNode;
979         }
980 
CloneTreeInternal(DataPointer other)981         private XmlNode CloneTreeInternal(DataPointer other)
982         {
983             Debug.Assert(_ignoreDataSetEvents == true);
984             Debug.Assert(_ignoreXmlEvents == true);
985             Debug.Assert(IsFoliationEnabled == false);
986 
987             // Create the diconnected tree based on the other navigator
988             XmlNode newNode = CloneNode(other);
989 
990             DataPointer dp = new DataPointer(other);
991             try
992             {
993                 dp.AddPointer();
994                 if (newNode.NodeType == XmlNodeType.Element)
995                 {
996                     int cAttributes = dp.AttributeCount;
997                     for (int i = 0; i < cAttributes; i++)
998                     {
999                         dp.MoveToOwnerElement();
1000                         if (dp.MoveToAttribute(i))
1001                         {
1002                             newNode.Attributes.Append((XmlAttribute)CloneTreeInternal(dp));
1003                         }
1004                     }
1005 
1006                     dp.MoveTo(other);
1007                 }
1008 
1009                 for (bool fMore = dp.MoveToFirstChild(); fMore; fMore = dp.MoveToNextSibling())
1010                     newNode.AppendChild(CloneTreeInternal(dp));
1011             }
1012             finally
1013             {
1014                 dp.SetNoLongerUse();
1015             }
1016 
1017             return newNode;
1018         }
1019 
CloneNode(bool deep)1020         public override XmlNode CloneNode(bool deep)
1021         {
1022             XmlDataDocument clone = (XmlDataDocument)(base.CloneNode(false));
1023             clone.Init(DataSet.Clone());
1024 
1025             clone._dataSet.EnforceConstraints = _dataSet.EnforceConstraints;
1026             Debug.Assert(clone.FirstChild == null);
1027             if (deep)
1028             {
1029                 DataPointer dp = new DataPointer(this, this);
1030                 try
1031                 {
1032                     dp.AddPointer();
1033                     for (bool fMore = dp.MoveToFirstChild(); fMore; fMore = dp.MoveToNextSibling())
1034                     {
1035                         XmlNode cloneNode;
1036                         if (dp.NodeType == XmlNodeType.Element)
1037                             cloneNode = clone.CloneTree(dp);
1038                         else
1039                             cloneNode = clone.CloneNode(dp);
1040                         clone.AppendChild(cloneNode);
1041                     }
1042                 }
1043                 finally
1044                 {
1045                     dp.SetNoLongerUse();
1046                 }
1047             }
1048 
1049             return clone;
1050         }
1051 
CloneNode(DataPointer dp)1052         private XmlNode CloneNode(DataPointer dp)
1053         {
1054             switch (dp.NodeType)
1055             {
1056                 //for the nodes without value and have no children
1057                 case XmlNodeType.DocumentFragment:
1058                     return CreateDocumentFragment();
1059                 case XmlNodeType.DocumentType:
1060                     return CreateDocumentType(dp.Name, dp.PublicId, dp.SystemId, dp.InternalSubset);
1061                 case XmlNodeType.XmlDeclaration:
1062                     return CreateXmlDeclaration(dp.Version, dp.Encoding, dp.Standalone);
1063 
1064                 //for the nodes with value but no children
1065                 case XmlNodeType.Text:
1066                     return CreateTextNode(dp.Value);
1067                 case XmlNodeType.CDATA:
1068                     return CreateCDataSection(dp.Value);
1069                 case XmlNodeType.ProcessingInstruction:
1070                     return CreateProcessingInstruction(dp.Name, dp.Value);
1071                 case XmlNodeType.Comment:
1072                     return CreateComment(dp.Value);
1073                 case XmlNodeType.Whitespace:
1074                     return CreateWhitespace(dp.Value);
1075                 case XmlNodeType.SignificantWhitespace:
1076                     return CreateSignificantWhitespace(dp.Value);
1077                 //for the nodes that don't have values, but might have children -- only clone the node and leave the children untouched
1078                 case XmlNodeType.Element:
1079                     return CreateElement(dp.Prefix, dp.LocalName, dp.NamespaceURI);
1080                 case XmlNodeType.Attribute:
1081                     return CreateAttribute(dp.Prefix, dp.LocalName, dp.NamespaceURI);
1082                 case XmlNodeType.EntityReference:
1083                     return CreateEntityReference(dp.Name);
1084             }
1085             throw new InvalidOperationException(SR.Format(SR.DataDom_CloneNode, dp.NodeType.ToString()));
1086         }
1087 
IsTextLikeNode(XmlNode n)1088         internal static bool IsTextLikeNode(XmlNode n)
1089         {
1090             switch (n.NodeType)
1091             {
1092                 case XmlNodeType.Text:
1093                 case XmlNodeType.CDATA:
1094                 case XmlNodeType.Whitespace:
1095                 case XmlNodeType.SignificantWhitespace:
1096                     return true;
1097 
1098                 case XmlNodeType.EntityReference:
1099                     Debug.Assert(false);
1100                     return false;
1101 
1102                 default:
1103                     return false;
1104             }
1105         }
1106 
IsNotMapped(DataColumn c)1107         internal bool IsNotMapped(DataColumn c)
1108         {
1109             return DataSetMapper.IsNotMapped(c);
1110         }
1111 
IsSame(DataColumn c, int recNo1, int recNo2)1112         private bool IsSame(DataColumn c, int recNo1, int recNo2)
1113         {
1114             if (c.Compare(recNo1, recNo2) == 0)
1115                 return true;
1116 
1117             return false;
1118         }
1119 
IsTextOnly(DataColumn c)1120         internal bool IsTextOnly(DataColumn c)
1121         {
1122             return c.ColumnMapping == MappingType.SimpleContent;
1123         }
1124 
1125 
1126         /// <summary>
1127         /// Loads the XML document from the specified file.
1128         /// </summary>
Load(string filename)1129         public override void Load(string filename)
1130         {
1131             _bForceExpandEntity = true;
1132             base.Load(filename);
1133             _bForceExpandEntity = false;
1134         }
1135 
1136         /// <summary>
1137         /// Loads the XML document from the specified Stream.
1138         /// </summary>
Load(Stream inStream)1139         public override void Load(Stream inStream)
1140         {
1141             _bForceExpandEntity = true;
1142             base.Load(inStream);
1143             _bForceExpandEntity = false;
1144         }
1145 
1146         /// <summary>
1147         /// Loads the XML document from the specified TextReader.
1148         /// </summary>
Load(TextReader txtReader)1149         public override void Load(TextReader txtReader)
1150         {
1151             _bForceExpandEntity = true;
1152             base.Load(txtReader);
1153             _bForceExpandEntity = false;
1154         }
1155 
1156         /// <summary>
1157         /// Loads the XML document from the specified XmlReader.
1158         /// </summary>
Load(XmlReader reader)1159         public override void Load(XmlReader reader)
1160         {
1161             if (FirstChild != null)
1162                 throw new InvalidOperationException(SR.DataDom_MultipleLoad);
1163 
1164             try
1165             {
1166                 _ignoreXmlEvents = true;
1167 
1168                 // Unhook the DataRowCreatedSpecial listener, since we no longer base on the first created DataRow to do the Bind
1169                 if (_fDataRowCreatedSpecial)
1170                     UnBindSpecialListeners();
1171 
1172                 // We should NOT create DataRow objects when calling XmlDataDocument.CreateElement
1173                 _fAssociateDataRow = false;
1174                 // Foliation s/b disabled
1175                 _isFoliationEnabled = false;
1176 
1177                 //now if we load from file we need to set the ExpandEntity flag to ExpandEntities
1178                 if (_bForceExpandEntity)
1179                 {
1180                     Debug.Assert(reader is XmlTextReader);
1181                     ((XmlTextReader)reader).EntityHandling = EntityHandling.ExpandEntities;
1182                 }
1183                 base.Load(reader);
1184                 BindForLoad();
1185             }
1186             finally
1187             {
1188                 _ignoreXmlEvents = false;
1189                 _isFoliationEnabled = true;
1190                 _autoFoliationState = ElementState.StrongFoliation;
1191                 _fAssociateDataRow = true;
1192             }
1193         }
1194 
LoadDataSetFromTree()1195         private void LoadDataSetFromTree()
1196         {
1197             _ignoreDataSetEvents = true;
1198             _ignoreXmlEvents = true;
1199             bool wasFoliationEnabled = IsFoliationEnabled;
1200             IsFoliationEnabled = false;
1201             bool saveEnforce = _dataSet.EnforceConstraints;
1202             _dataSet.EnforceConstraints = false;
1203 
1204             try
1205             {
1206                 Debug.Assert(DocumentElement != null);
1207                 LoadRows(null, DocumentElement);
1208                 SyncRows(null, DocumentElement, true);
1209 
1210                 _dataSet.EnforceConstraints = saveEnforce;
1211             }
1212             finally
1213             {
1214                 _ignoreDataSetEvents = false;
1215                 _ignoreXmlEvents = false;
1216                 IsFoliationEnabled = wasFoliationEnabled;
1217             }
1218         }
1219 
LoadTreeFromDataSet(DataSet ds)1220         private void LoadTreeFromDataSet(DataSet ds)
1221         {
1222             _ignoreDataSetEvents = true;
1223             _ignoreXmlEvents = true;
1224             bool wasFoliationEnabled = IsFoliationEnabled;
1225             IsFoliationEnabled = false;
1226             _fAssociateDataRow = false;
1227 
1228             DataTable[] orderedTables = OrderTables(ds);
1229             // problem is after we add support for Namespace  for DataTable, when infering we do not guarantee that table would be
1230             // in the same sequence that they were in XML because of namespace, some would be on different schema, so since they
1231             // won't be in the same sequence as in XML, we may end up with having a child table, before its parent (which is not doable
1232             // with XML; and this happend because they are in different namespace)
1233             // this kind of problems are known and please see comment in "OnNestedParentChange"
1234             // so to fix it in general, we try to iterate over ordered tables instead of going over all tables in DataTableCollection with their own sequence
1235 
1236             try
1237             {
1238                 for (int i = 0; i < orderedTables.Length; i++)
1239                 {
1240                     DataTable t = orderedTables[i];
1241                     foreach (DataRow r in t.Rows)
1242                     {
1243                         Debug.Assert(r.Element == null);
1244                         XmlBoundElement rowElem = AttachBoundElementToDataRow(r);
1245 
1246                         switch (r.RowState)
1247                         {
1248                             case DataRowState.Added:
1249                             case DataRowState.Unchanged:
1250                             case DataRowState.Modified:
1251                                 OnAddRow(r);
1252                                 break;
1253                             case DataRowState.Deleted:
1254                                 // Nothing to do (the row already has an associated element as a fragment
1255                                 break;
1256                             case DataRowState.Detached:
1257                                 // We should not get rows in this state
1258                                 Debug.Assert(false);
1259                                 break;
1260                             default:
1261                                 // Unknown row state
1262                                 Debug.Assert(false);
1263                                 break;
1264                         }
1265                     }
1266                 }
1267             }
1268             finally
1269             {
1270                 _ignoreDataSetEvents = false;
1271                 _ignoreXmlEvents = false;
1272                 IsFoliationEnabled = wasFoliationEnabled;
1273                 _fAssociateDataRow = true;
1274             }
1275         }
1276 
1277         // load all data from tree structre into datarows
LoadRows(XmlBoundElement rowElem, XmlNode node)1278         private void LoadRows(XmlBoundElement rowElem, XmlNode node)
1279         {
1280             Debug.Assert(node != null);
1281 
1282             XmlBoundElement be = node as XmlBoundElement;
1283             if (be != null)
1284             {
1285                 DataTable dt = _mapper.SearchMatchingTableSchema(rowElem, be);
1286 
1287                 if (dt != null)
1288                 {
1289                     DataRow r = GetRowFromElement(be);
1290                     Debug.Assert(r == null);
1291                     // If the rowElement was just created and has an un-initialized
1292                     if (be.ElementState == ElementState.None)
1293                         be.ElementState = ElementState.WeakFoliation;
1294                     r = dt.CreateEmptyRow();
1295                     Bind(r, be);
1296 
1297                     // the region rowElem is now be
1298                     Debug.Assert(be.Row != null);
1299                     rowElem = be;
1300                 }
1301             }
1302             // recurse down for children
1303             for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
1304                 LoadRows(rowElem, child);
1305         }
1306 
1307         internal DataSetMapper Mapper
1308         {
1309             get
1310             {
1311                 return _mapper;
1312             }
1313         }
1314 
OnDataRowCreated(object oDataSet, DataRow row)1315         internal void OnDataRowCreated(object oDataSet, DataRow row)
1316         {
1317             Debug.Assert(row.RowState == DataRowState.Detached);
1318             OnNewRow(row);
1319         }
1320 
OnClearCalled(object oDataSet, DataTable table)1321         internal void OnClearCalled(object oDataSet, DataTable table)
1322         {
1323             throw new NotSupportedException(SR.DataDom_NotSupport_Clear);
1324         }
1325 
OnDataRowCreatedSpecial(object oDataSet, DataRow row)1326         internal void OnDataRowCreatedSpecial(object oDataSet, DataRow row)
1327         {
1328             Debug.Assert(row.RowState == DataRowState.Detached);
1329 
1330             // Register the regular events and un-register this one
1331             Bind(true);
1332             // Pass the event to the regular listener
1333             OnNewRow(row);
1334         }
1335         // Called when a new DataRow is created
OnNewRow(DataRow row)1336         internal void OnNewRow(DataRow row)
1337         {
1338             Debug.Assert(row.Element == null);
1339             // Allow New state also because we are calling this function from
1340             Debug.Assert(row.RowState == DataRowState.Detached);
1341 
1342             AttachBoundElementToDataRow(row);
1343         }
1344 
AttachBoundElementToDataRow(DataRow row)1345         private XmlBoundElement AttachBoundElementToDataRow(DataRow row)
1346         {
1347             Debug.Assert(row.Element == null);
1348             DataTable table = row.Table;
1349             // We shoould NOT call CreateElement here, since CreateElement will create and attach a new DataRow to the element
1350             XmlBoundElement rowElement = new XmlBoundElement(string.Empty, table.EncodedTableName, table.Namespace, this);
1351             rowElement.IsEmpty = false;
1352             Bind(row, rowElement);
1353             rowElement.ElementState = ElementState.Defoliated;
1354             return rowElement;
1355         }
1356 
NeedXSI_NilAttr(DataRow row)1357         private bool NeedXSI_NilAttr(DataRow row)
1358         {
1359             DataTable tb = row.Table;
1360             Debug.Assert(tb != null);
1361             if (tb._xmlText == null)
1362                 return false;
1363             object value = row[tb._xmlText];
1364             return (Convert.IsDBNull(value));
1365         }
1366 
OnAddRow(DataRow row)1367         private void OnAddRow(DataRow row)
1368         {
1369             // Xml operations in this func should not trigger ROM operations
1370             Debug.Assert(_ignoreXmlEvents == true);
1371 
1372             XmlBoundElement rowElement = (XmlBoundElement)(GetElementFromRow(row));
1373             Debug.Assert(rowElement != null);
1374 
1375             if (NeedXSI_NilAttr(row) && !rowElement.IsFoliated)
1376                 //we need to foliate it because we need to add one more attribute xsi:nil = true;
1377                 ForceFoliation(rowElement, AutoFoliationState);
1378 
1379             Debug.Assert(rowElement != null);
1380             DataRow rowDocElem = GetRowFromElement(DocumentElement);
1381             if (rowDocElem != null)
1382             {
1383                 DataRow parentRow = GetNestedParent(row);
1384                 if (parentRow == null)
1385                     DemoteDocumentElement();
1386             }
1387             EnsureDocumentElement().AppendChild(rowElement);
1388 
1389             // Move the children of the row under
1390             FixNestedChildren(row, rowElement);
1391             OnNestedParentChange(row, rowElement, null);
1392         }
1393 
OnColumnValueChanged(DataRow row, DataColumn col, XmlBoundElement rowElement)1394         private void OnColumnValueChanged(DataRow row, DataColumn col, XmlBoundElement rowElement)
1395         {
1396             if (IsNotMapped(col))
1397             {
1398                 goto lblDoNestedRelationSync;
1399             }
1400 
1401             object value = row[col];
1402 
1403             if (col.ColumnMapping == MappingType.SimpleContent && Convert.IsDBNull(value) && !rowElement.IsFoliated)
1404             {
1405                 ForceFoliation(rowElement, ElementState.WeakFoliation);
1406             }
1407             else
1408             {
1409                 // no need to sync if not foliated
1410                 if (!IsFoliated(rowElement))
1411                 {
1412 #if DEBUG
1413                     // If the new value is null, we should be already foliated if there is a DataPointer that points to the column
1414                     // (see OnRowChanging, case DataRowAction.Change)
1415                     if (Convert.IsDBNull(row[col, DataRowVersion.Current]))
1416                     {
1417                         try
1418                         {
1419                             if (_pointers.Count > 0)
1420                             {
1421                                 object pointer = null;
1422                                 foreach (DictionaryEntry entry in _pointers)
1423                                 {
1424                                     pointer = entry.Value;
1425                                     Debug.Assert((pointer != null) && !((IXmlDataVirtualNode)pointer).IsOnColumn(col));
1426                                 }
1427                             }
1428                         }
1429                         catch (Exception e) when (Data.Common.ADP.IsCatchableExceptionType(e))
1430                         {
1431                             // We may get an exception if we are in foreach and a new pointer has been added to this.pointers. When this happens, we will skip this check and ignore the exceptions
1432                         }
1433                     }
1434 #endif
1435                     goto lblDoNestedRelationSync;
1436                 }
1437             }
1438 
1439             if (IsTextOnly(col))
1440             {
1441                 if (Convert.IsDBNull(value))
1442                 {
1443                     value = string.Empty;
1444                     //make sure that rowElement has Attribute xsi:nil and its value is true
1445                     XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL);
1446                     if (attr == null)
1447                     {
1448                         attr = CreateAttribute(XSI, Keywords.XSI_NIL, Keywords.XSINS);
1449                         attr.Value = Keywords.TRUE;
1450                         rowElement.SetAttributeNode(attr);
1451                         _bHasXSINIL = true;
1452                     }
1453                     else
1454                         attr.Value = Keywords.TRUE;
1455                 }
1456                 else
1457                 {
1458                     //make sure that if rowElement has Attribute xsi:nil, its value is false
1459                     XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL);
1460                     if (attr != null)
1461                         attr.Value = Keywords.FALSE;
1462                 }
1463                 ReplaceInitialChildText(rowElement, col.ConvertObjectToXml(value));
1464                 goto lblDoNestedRelationSync;
1465             }
1466 
1467             // update the attribute that maps to the column
1468             bool fFound = false;
1469 
1470             // Find the field node and set it's value
1471             if (col.ColumnMapping == MappingType.Attribute)
1472             {
1473                 foreach (XmlAttribute attr in rowElement.Attributes)
1474                 {
1475                     if (attr.LocalName == col.EncodedColumnName && attr.NamespaceURI == col.Namespace)
1476                     {
1477                         if (Convert.IsDBNull(value))
1478                         {
1479                             attr.OwnerElement.Attributes.Remove(attr);
1480                         }
1481                         else
1482                         {
1483                             attr.Value = col.ConvertObjectToXml(value);
1484                         }
1485                         fFound = true;
1486                         break;
1487                     }
1488                 }
1489 
1490                 // create new attribute if we didn't find one.
1491                 if (!fFound && !Convert.IsDBNull(value))
1492                 {
1493                     rowElement.SetAttribute(col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml(value));
1494                 }
1495             }
1496             else
1497             {
1498                 // update elements that map to the column...
1499                 RegionIterator iter = new RegionIterator(rowElement);
1500                 bool fMore = iter.Next();
1501                 while (fMore)
1502                 {
1503                     if (iter.CurrentNode.NodeType == XmlNodeType.Element)
1504                     {
1505                         XmlElement e = (XmlElement)iter.CurrentNode;
1506                         Debug.Assert(e != null);
1507                         //we should skip the subregion
1508                         XmlBoundElement be = e as XmlBoundElement;
1509                         if (be != null && be.Row != null)
1510                         {
1511                             fMore = iter.NextRight(); //skip over the sub-region
1512                             continue;
1513                         }
1514                         if (e.LocalName == col.EncodedColumnName && e.NamespaceURI == col.Namespace)
1515                         {
1516                             fFound = true;
1517                             if (Convert.IsDBNull(value))
1518                             {
1519                                 PromoteNonValueChildren(e);
1520                                 fMore = iter.NextRight();
1521                                 e.ParentNode.RemoveChild(e);
1522                                 // keep looking for more matching elements
1523                                 continue;
1524                             }
1525                             else
1526                             {
1527                                 ReplaceInitialChildText(e, col.ConvertObjectToXml(value));
1528                                 //make sure that if the Element has Attribute xsi:nil, its value is false
1529                                 XmlAttribute attr = e.GetAttributeNode(XSI_NIL);
1530                                 if (attr != null)
1531                                     attr.Value = Keywords.FALSE;
1532                                 // no need to look any further.
1533                                 goto lblDoNestedRelationSync;
1534                             }
1535                         }
1536                     }
1537                     fMore = iter.Next();
1538                 }
1539 
1540                 // create new element if we didn't find one.
1541                 if (!fFound && !Convert.IsDBNull(value))
1542                 {
1543                     XmlElement newElem = new XmlBoundElement(string.Empty, col.EncodedColumnName, col.Namespace, this);
1544                     newElem.AppendChild(CreateTextNode(col.ConvertObjectToXml(value)));
1545 
1546                     XmlNode elemBefore = GetColumnInsertAfterLocation(row, col, rowElement);
1547                     if (elemBefore != null)
1548                     {
1549                         rowElement.InsertAfter(newElem, elemBefore);
1550                     }
1551                     else if (rowElement.FirstChild != null)
1552                     {
1553                         rowElement.InsertBefore(newElem, rowElement.FirstChild);
1554                     }
1555                     else
1556                     {
1557                         rowElement.AppendChild(newElem);
1558                     }
1559                 }
1560             }
1561         lblDoNestedRelationSync:
1562             // Change the XML to conform to the (potentially) change in parent nested relation
1563             DataRelation relation = GetNestedParentRelation(row);
1564             if (relation != null)
1565             {
1566                 Debug.Assert(relation.ChildTable == row.Table);
1567                 if (relation.ChildKey.ContainsColumn(col))
1568                     OnNestedParentChange(row, rowElement, col);
1569             }
1570         }
1571 
OnColumnChanged(object sender, DataColumnChangeEventArgs args)1572         private void OnColumnChanged(object sender, DataColumnChangeEventArgs args)
1573         {
1574             // You should not be able to make DataRow field changes if the DataRow is deleted
1575             Debug.Assert(args.Row.RowState != DataRowState.Deleted);
1576 
1577             if (_ignoreDataSetEvents)
1578                 return;
1579 
1580             bool wasIgnoreXmlEvents = _ignoreXmlEvents;
1581             _ignoreXmlEvents = true;
1582             bool wasFoliationEnabled = IsFoliationEnabled;
1583             IsFoliationEnabled = false;
1584 
1585             try
1586             {
1587                 DataRow row = args.Row;
1588                 DataColumn col = args.Column;
1589                 object oVal = args.ProposedValue;
1590 
1591                 if (row.RowState == DataRowState.Detached)
1592                 {
1593                     XmlBoundElement be = row.Element;
1594                     Debug.Assert(be != null);
1595                     if (be.IsFoliated)
1596                     {
1597                         // Need to sync changes from ROM to DOM
1598                         OnColumnValueChanged(row, col, be);
1599                     }
1600                 }
1601             }
1602             finally
1603             {
1604                 IsFoliationEnabled = wasFoliationEnabled;
1605                 _ignoreXmlEvents = wasIgnoreXmlEvents;
1606             }
1607         }
1608 
OnColumnValuesChanged(DataRow row, XmlBoundElement rowElement)1609         private void OnColumnValuesChanged(DataRow row, XmlBoundElement rowElement)
1610         {
1611             Debug.Assert(row != null);
1612             Debug.Assert(rowElement != null);
1613 
1614             // If user has cascading relationships, then columnChangeList will contains the changed columns only for the last row beeing cascaded
1615             // but there will be multiple ROM events
1616             if (_columnChangeList.Count > 0)
1617             {
1618                 if (((DataColumn)(_columnChangeList[0])).Table == row.Table)
1619                 {
1620                     foreach (DataColumn c in _columnChangeList)
1621                         OnColumnValueChanged(row, c, rowElement);
1622                 }
1623                 else
1624                 {
1625                     foreach (DataColumn c in row.Table.Columns)
1626                         OnColumnValueChanged(row, c, rowElement);
1627                 }
1628             }
1629             else
1630             {
1631                 foreach (DataColumn c in row.Table.Columns)
1632                     OnColumnValueChanged(row, c, rowElement);
1633             }
1634             _columnChangeList.Clear();
1635         }
1636 
OnDeleteRow(DataRow row, XmlBoundElement rowElement)1637         private void OnDeleteRow(DataRow row, XmlBoundElement rowElement)
1638         {
1639             // IgnoreXmlEvents s/b on since we are manipulating the XML tree and we not want this to reflect in ROM view.
1640             Debug.Assert(_ignoreXmlEvents == true);
1641             // Special case when rowElem is document element: we create a new docElem, move the current one as a child of
1642             // the new created docElem, then process as if the docElem is not a rowElem
1643             if (rowElement == DocumentElement)
1644                 DemoteDocumentElement();
1645 
1646             PromoteInnerRegions(rowElement);
1647             rowElement.ParentNode.RemoveChild(rowElement);
1648         }
1649 
OnDeletingRow(DataRow row, XmlBoundElement rowElement)1650         private void OnDeletingRow(DataRow row, XmlBoundElement rowElement)
1651         {
1652             // Note that this function is beeing called even if ignoreDataSetEvents == true.
1653 
1654             // Foliate, so we can be able to preserve the nodes even if the DataRow has no longer values for the crtRecord.
1655             if (IsFoliated(rowElement))
1656                 return;
1657 
1658             bool wasIgnoreXmlEvents = IgnoreXmlEvents;
1659             IgnoreXmlEvents = true;
1660             bool wasFoliationEnabled = IsFoliationEnabled;
1661             IsFoliationEnabled = true;
1662             try
1663             {
1664                 Foliate(rowElement);
1665             }
1666             finally
1667             {
1668                 IsFoliationEnabled = wasFoliationEnabled;
1669                 IgnoreXmlEvents = wasIgnoreXmlEvents;
1670             }
1671         }
1672 
OnFoliated(XmlNode node)1673         private void OnFoliated(XmlNode node)
1674         {
1675             while (true)
1676             {
1677                 try
1678                 {
1679                     if (_pointers.Count > 0)
1680                     {
1681                         foreach (DictionaryEntry entry in _pointers)
1682                         {
1683                             object pointer = entry.Value;
1684                             Debug.Assert(pointer != null);
1685                             ((IXmlDataVirtualNode)pointer).OnFoliated(node);
1686                         }
1687                     }
1688                     return;
1689                 }
1690                 catch (Exception e) when (Data.Common.ADP.IsCatchableExceptionType(e))
1691                 {
1692                     // This can happens only when some threads are creating navigators (thus modifying this.pointers) while other threads are in the foreach loop.
1693                     // Solution is to re-try OnFoliated.
1694                 }
1695             }
1696             // You should never get here in regular cases
1697         }
1698 
FindAssociatedParentColumn(DataRelation relation, DataColumn childCol)1699         private DataColumn FindAssociatedParentColumn(DataRelation relation, DataColumn childCol)
1700         {
1701             DataColumn[] columns = relation.ChildKey.ColumnsReference;
1702             for (int i = 0; i < columns.Length; i++)
1703             {
1704                 if (childCol == columns[i])
1705                     return relation.ParentKey.ColumnsReference[i];
1706             }
1707             return null;
1708         }
1709 
1710         // Change the childElement position in the tree to conform to the parent nested relationship in ROM
OnNestedParentChange(DataRow child, XmlBoundElement childElement, DataColumn childCol)1711         private void OnNestedParentChange(DataRow child, XmlBoundElement childElement, DataColumn childCol)
1712         {
1713             Debug.Assert(child.Element == childElement && childElement.Row == child);
1714             // This function is (and s/b) called as a result of ROM changes, therefore XML changes done here should not be sync-ed to ROM
1715             Debug.Assert(_ignoreXmlEvents == true);
1716 #if DEBUG
1717             // In order to check that this move does not change the connected/disconnected state of the node
1718             bool fChildElementConnected = IsConnected(childElement);
1719 #endif
1720             DataRow parentRowInTree;
1721             if (childElement == DocumentElement || childElement.ParentNode == null)
1722                 parentRowInTree = null;
1723             else
1724                 parentRowInTree = GetRowFromElement((XmlElement)childElement.ParentNode);
1725             DataRow parentRowInRelation = GetNestedParent(child);
1726 
1727             if (parentRowInTree != parentRowInRelation)
1728             {
1729                 if (parentRowInRelation != null)
1730                 {
1731                     XmlElement newParent = GetElementFromRow(parentRowInRelation);
1732                     newParent.AppendChild(childElement);
1733                 }
1734                 else
1735                 {
1736                     // no parent? Maybe the parentRow is during changing or childCol is the ID is set to null ( detached from the parent row ).
1737                     DataRelation relation = GetNestedParentRelation(child);
1738                     if (childCol == null || relation == null || Convert.IsDBNull(child[childCol]))
1739                     {
1740                         EnsureNonRowDocumentElement().AppendChild(childElement);
1741                     }
1742                     else
1743                     {
1744                         DataColumn colInParent = FindAssociatedParentColumn(relation, childCol);
1745                         Debug.Assert(colInParent != null);
1746                         object comparedValue = colInParent.ConvertValue(child[childCol]);
1747                         if (parentRowInTree._tempRecord != -1 && colInParent.CompareValueTo(parentRowInTree._tempRecord, comparedValue) != 0)
1748                         {
1749                             EnsureNonRowDocumentElement().AppendChild(childElement);
1750                         }
1751                         //else do nothing because its original parentRowInRelation will be changed so that this row will still be its child
1752                     }
1753                 }
1754             }
1755 #if DEBUG
1756             // We should not have changed the connected/disconnected state of the node (since the row state did not change) -- IOW if the original childElem was in dis-connected
1757             // state and corresponded to a detached/deleted row, by adding it to the main tree we become inconsistent (since we have now a deleted/detached row in the main tree)
1758             // Same goes when we remove a node from connected tree to make it a child of a row-node corresponding to a non-live row.
1759             Debug.Assert(fChildElementConnected == IsConnected(childElement));
1760             Debug.Assert(IsRowLive(child) ? IsConnected(childElement) : !IsConnected(childElement));
1761 #endif
1762         }
1763 
OnNodeChanged(object sender, XmlNodeChangedEventArgs args)1764         private void OnNodeChanged(object sender, XmlNodeChangedEventArgs args)
1765         {
1766             if (_ignoreXmlEvents)
1767                 return;
1768 
1769             bool wasIgnoreDataSetEvents = _ignoreDataSetEvents;
1770             bool wasIgnoreXmlEvents = _ignoreXmlEvents;
1771             bool wasFoliationEnabled = IsFoliationEnabled;
1772             _ignoreDataSetEvents = true;
1773             _ignoreXmlEvents = true;
1774             IsFoliationEnabled = false;
1775             bool fEnableCascading = DataSet._fEnableCascading;
1776             DataSet._fEnableCascading = false;
1777 
1778             try
1779             {
1780                 // okay to allow text node value changes when bound.
1781                 XmlBoundElement rowElement = null;
1782 
1783                 Debug.Assert(DataSet.EnforceConstraints == false);
1784 
1785                 if (_mapper.GetRegion(args.Node, out rowElement))
1786                 {
1787                     SynchronizeRowFromRowElement(rowElement);
1788                 }
1789             }
1790             finally
1791             {
1792                 _ignoreDataSetEvents = wasIgnoreDataSetEvents;
1793                 _ignoreXmlEvents = wasIgnoreXmlEvents;
1794                 IsFoliationEnabled = wasFoliationEnabled;
1795                 DataSet._fEnableCascading = fEnableCascading;
1796             }
1797         }
1798 
OnNodeChanging(object sender, XmlNodeChangedEventArgs args)1799         private void OnNodeChanging(object sender, XmlNodeChangedEventArgs args)
1800         {
1801             if (_ignoreXmlEvents)
1802                 return;
1803             if (DataSet.EnforceConstraints != false)
1804                 throw new InvalidOperationException(SR.DataDom_EnforceConstraintsShouldBeOff);
1805         }
1806 
1807 
OnNodeInserted(object sender, XmlNodeChangedEventArgs args)1808         private void OnNodeInserted(object sender, XmlNodeChangedEventArgs args)
1809         {
1810             if (_ignoreXmlEvents)
1811                 return;
1812 
1813             bool wasIgnoreDataSetEvents = _ignoreDataSetEvents;
1814             bool wasIgnoreXmlEvents = _ignoreXmlEvents;
1815             bool wasFoliationEnabled = IsFoliationEnabled;
1816             _ignoreDataSetEvents = true;
1817             _ignoreXmlEvents = true;
1818             IsFoliationEnabled = false;
1819 
1820             Debug.Assert(DataSet.EnforceConstraints == false);
1821 
1822             bool fEnableCascading = DataSet._fEnableCascading;
1823             DataSet._fEnableCascading = false;
1824 
1825             try
1826             {
1827                 // Handle both new node inserted and 2nd part of a move operation.
1828                 XmlNode node = args.Node;
1829                 XmlNode oldParent = args.OldParent;
1830                 XmlNode newParent = args.NewParent;
1831 
1832                 // The code bellow assumes a move operation is fired by DOM in 2 steps: a Remvoe followed by an Insert - this is the 2nd part, the Insert.
1833                 Debug.Assert(oldParent == null);
1834                 if (IsConnected(newParent))
1835                 {
1836                     // Inserting a node to connected tree
1837                     OnNodeInsertedInTree(node);
1838                 }
1839                 else
1840                 {
1841                     // Inserting a node to disconnected tree
1842                     OnNodeInsertedInFragment(node);
1843                 }
1844             }
1845             finally
1846             {
1847                 _ignoreDataSetEvents = wasIgnoreDataSetEvents;
1848                 _ignoreXmlEvents = wasIgnoreXmlEvents;
1849                 IsFoliationEnabled = wasFoliationEnabled;
1850                 DataSet._fEnableCascading = fEnableCascading;
1851             }
1852         }
1853 
OnNodeInserting(object sender, XmlNodeChangedEventArgs args)1854         private void OnNodeInserting(object sender, XmlNodeChangedEventArgs args)
1855         {
1856             if (_ignoreXmlEvents)
1857                 return;
1858             if (DataSet.EnforceConstraints != false)
1859                 throw new InvalidOperationException(SR.DataDom_EnforceConstraintsShouldBeOff);
1860         }
1861 
1862 
OnNodeRemoved(object sender, XmlNodeChangedEventArgs args)1863         private void OnNodeRemoved(object sender, XmlNodeChangedEventArgs args)
1864         {
1865             if (_ignoreXmlEvents)
1866                 return;
1867 
1868             bool wasIgnoreDataSetEvents = _ignoreDataSetEvents;
1869             bool wasIgnoreXmlEvents = _ignoreXmlEvents;
1870             bool wasFoliationEnabled = IsFoliationEnabled;
1871             _ignoreDataSetEvents = true;
1872             _ignoreXmlEvents = true;
1873             IsFoliationEnabled = false;
1874 
1875             Debug.Assert(DataSet.EnforceConstraints == false);
1876 
1877             bool fEnableCascading = DataSet._fEnableCascading;
1878             DataSet._fEnableCascading = false;
1879 
1880             try
1881             {
1882                 XmlNode node = args.Node;
1883                 XmlNode oldParent = args.OldParent;
1884                 Debug.Assert(args.NewParent == null);
1885 
1886                 if (IsConnected(oldParent))
1887                 {
1888                     // Removing from connected tree to disconnected tree
1889                     OnNodeRemovedFromTree(node, oldParent);
1890                 }
1891                 else
1892                 {
1893                     // Removing from disconnected tree to disconnected tree: just sync the old region
1894                     OnNodeRemovedFromFragment(node, oldParent);
1895                 }
1896             }
1897             finally
1898             {
1899                 _ignoreDataSetEvents = wasIgnoreDataSetEvents;
1900                 _ignoreXmlEvents = wasIgnoreXmlEvents;
1901                 IsFoliationEnabled = wasFoliationEnabled;
1902                 DataSet._fEnableCascading = fEnableCascading;
1903             }
1904         }
1905 
OnNodeRemoving(object sender, XmlNodeChangedEventArgs args)1906         private void OnNodeRemoving(object sender, XmlNodeChangedEventArgs args)
1907         {
1908             if (_ignoreXmlEvents)
1909                 return;
1910             if (DataSet.EnforceConstraints != false)
1911                 throw new InvalidOperationException(SR.DataDom_EnforceConstraintsShouldBeOff);
1912         }
1913 
1914         // Node was removed from connected tree to disconnected tree
OnNodeRemovedFromTree(XmlNode node, XmlNode oldParent)1915         private void OnNodeRemovedFromTree(XmlNode node, XmlNode oldParent)
1916         {
1917             XmlBoundElement oldRowElem;
1918 
1919             // Synchronize values from old region
1920             if (_mapper.GetRegion(oldParent, out oldRowElem))
1921                 SynchronizeRowFromRowElement(oldRowElem);
1922 
1923             // Disconnect all regions, starting w/ node (if it is a row-elem)
1924             XmlBoundElement rowElem = node as XmlBoundElement;
1925             if (rowElem != null && rowElem.Row != null)
1926                 EnsureDisconnectedDataRow(rowElem);
1927             TreeIterator iter = new TreeIterator(node);
1928             for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement())
1929             {
1930                 rowElem = (XmlBoundElement)(iter.CurrentNode);
1931                 EnsureDisconnectedDataRow(rowElem);
1932             }
1933 
1934             // Assert that all sub-regions are disconnected
1935             AssertNonLiveRows(node);
1936         }
1937         // Node was removed from the disconnected tree to disconnected tree
OnNodeRemovedFromFragment(XmlNode node, XmlNode oldParent)1938         private void OnNodeRemovedFromFragment(XmlNode node, XmlNode oldParent)
1939         {
1940             XmlBoundElement oldRowElem;
1941 
1942             if (_mapper.GetRegion(oldParent, out oldRowElem))
1943             {
1944                 // Sync the old region if it is not deleted
1945                 DataRow row = oldRowElem.Row;
1946                 // Since the old old region was disconnected, then the row can be only Deleted or Detached
1947                 Debug.Assert(!IsRowLive(row));
1948                 if (oldRowElem.Row.RowState == DataRowState.Detached)
1949                     SynchronizeRowFromRowElement(oldRowElem);
1950             }
1951 
1952             // Need to set nested for the sub-regions (if node is a row-elem, we need to set it just for itself)
1953             XmlBoundElement be = node as XmlBoundElement;
1954             if (be != null && be.Row != null)
1955             {
1956                 Debug.Assert(!IsRowLive(be.Row));
1957                 SetNestedParentRegion(be, null);
1958             }
1959             else
1960             {
1961                 // Set nested parent to null for all child regions
1962                 TreeIterator iter = new TreeIterator(node);
1963                 for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement())
1964                 {
1965                     XmlBoundElement rowElemChild = (XmlBoundElement)(iter.CurrentNode);
1966                     SetNestedParentRegion(rowElemChild, null);
1967                 }
1968             }
1969 
1970             // Assert that all sub-regions are disconnected
1971             AssertNonLiveRows(node);
1972         }
1973 
1974 
OnRowChanged(object sender, DataRowChangeEventArgs args)1975         private void OnRowChanged(object sender, DataRowChangeEventArgs args)
1976         {
1977             if (_ignoreDataSetEvents)
1978                 return;
1979 
1980             _ignoreXmlEvents = true;
1981             bool wasFoliationEnabled = IsFoliationEnabled;
1982             IsFoliationEnabled = false;
1983 
1984             try
1985             {
1986                 DataRow row = args.Row;
1987                 XmlBoundElement rowElement = row.Element;
1988                 // We should have an associated row-elem created when the DataRow was created (or at the load time)
1989                 Debug.Assert(rowElement != null);
1990 
1991                 switch (args.Action)
1992                 {
1993                     case DataRowAction.Add:
1994                         OnAddRow(row);
1995                         break;
1996 
1997                     case DataRowAction.Delete:
1998                         OnDeleteRow(row, rowElement);
1999                         break;
2000 
2001                     case DataRowAction.Rollback:
2002                         switch (_rollbackState)
2003                         {
2004                             case DataRowState.Deleted:
2005                                 OnUndeleteRow(row, rowElement);
2006                                 UpdateAllColumns(row, rowElement);
2007                                 break;
2008 
2009                             case DataRowState.Added:
2010                                 rowElement.ParentNode.RemoveChild(rowElement);
2011                                 break;
2012 
2013                             case DataRowState.Modified:
2014                                 OnColumnValuesChanged(row, rowElement);
2015                                 break;
2016                         }
2017                         break;
2018 
2019                     case DataRowAction.Change:
2020                         OnColumnValuesChanged(row, rowElement);
2021                         break;
2022 
2023                     case DataRowAction.Commit:
2024                         if (row.RowState == DataRowState.Detached)
2025                         {
2026                             //by now, all the descendent of the element that is not of this region should have been promoted already
2027                             rowElement.RemoveAll();
2028                         }
2029                         break;
2030                     default:
2031                         break;
2032                 }
2033             }
2034             finally
2035             {
2036                 IsFoliationEnabled = wasFoliationEnabled;
2037                 _ignoreXmlEvents = false;
2038             }
2039         }
2040 
OnRowChanging(object sender, DataRowChangeEventArgs args)2041         private void OnRowChanging(object sender, DataRowChangeEventArgs args)
2042         {
2043             // We foliate the region each time the assocaited row gets deleted
2044             DataRow row = args.Row;
2045             if (args.Action == DataRowAction.Delete && row.Element != null)
2046             {
2047                 OnDeletingRow(row, row.Element);
2048                 return;
2049             }
2050 
2051             if (_ignoreDataSetEvents)
2052                 return;
2053 
2054             bool wasFoliationEnabled = IsFoliationEnabled;
2055             IsFoliationEnabled = false;
2056 
2057             try
2058             {
2059                 _ignoreXmlEvents = true;
2060 
2061                 XmlElement rowElement = GetElementFromRow(row);
2062 
2063                 int nRec1 = -1;
2064                 int nRec2 = -1;
2065 
2066                 if (rowElement != null)
2067                 {
2068                     switch (args.Action)
2069                     {
2070                         case DataRowAction.Add:
2071                             // DataRow is beeing added to the table (Table.Rows.Add is beeing called)
2072                             break;
2073 
2074                         case DataRowAction.Delete:
2075                             // DataRow is beeing deleted
2076                             //    - state transition from New (AKA PendingInsert) to Detached (AKA Created)
2077                             //    - state transition from Unchanged to Deleted (AKA PendingDelete)
2078                             //    - state transition from Modified (AKA PendingChange) to Delete (AKA PendingDelete)
2079                             Debug.Assert(false);  // This should have been handled above, irrespective of ignoreDataSetEvents value (true or false)
2080                             break;
2081 
2082                         case DataRowAction.Rollback:
2083                             // DataRow gets reverted to previous values (by calling DataRow.RejectChanges):
2084                             //    - state transition from Detached (AKA Created) to Detached (AKA Created)
2085                             //    - state transition from New (AKA PendingInsert) to Detached (AKA Created)
2086                             //    - state transition from Modified (AKA PendingChange) to Unchanged
2087                             //    - state transition from Deleted (AKA PendingDelete) to Unchanged
2088                             _rollbackState = row.RowState;
2089                             switch (_rollbackState)
2090                             {
2091                                 case DataRowState.Deleted:
2092                                     break;
2093 
2094                                 case DataRowState.Detached:
2095                                     break;
2096 
2097                                 case DataRowState.Added:
2098                                     break;
2099 
2100                                 case DataRowState.Modified:
2101                                     _columnChangeList.Clear();
2102                                     nRec1 = row.GetRecordFromVersion(DataRowVersion.Original);
2103                                     nRec2 = row.GetRecordFromVersion(DataRowVersion.Current);
2104                                     foreach (DataColumn c in row.Table.Columns)
2105                                     {
2106                                         if (!IsSame(c, nRec1, nRec2))
2107                                             _columnChangeList.Add(c);
2108                                     }
2109                                     break;
2110                             }
2111                             break;
2112 
2113                         case DataRowAction.Change:
2114                             // A DataRow field is beeing changed
2115                             //    - state transition from New (AKA PendingInsert) to New (AKA PendingInsert)
2116                             //    - state transition from Unchanged to Modified (AKA PendingChange)
2117                             //    - state transition from Modified (AKA PendingChange) to Modified (AKA PendingChange)
2118                             _columnChangeList.Clear();
2119                             nRec1 = row.GetRecordFromVersion(DataRowVersion.Proposed);
2120                             nRec2 = row.GetRecordFromVersion(DataRowVersion.Current);
2121                             foreach (DataColumn c in row.Table.Columns)
2122                             {
2123                                 object proposedValue = row[c, DataRowVersion.Proposed];
2124                                 object currentValue = row[c, DataRowVersion.Current];
2125                                 // Foliate if proposedValue is DBNull; this way the DataPointer objects will point to a disconnected fragment after
2126                                 // the DBNull value is beeing set
2127                                 if (Convert.IsDBNull(proposedValue) && !Convert.IsDBNull(currentValue))
2128                                 {
2129                                     // Foliate only for non-hidden columns (since hidden cols are not represented in XML)
2130                                     if (c.ColumnMapping != MappingType.Hidden)
2131                                         FoliateIfDataPointers(row, rowElement);
2132                                 }
2133                                 if (!IsSame(c, nRec1, nRec2))
2134                                     _columnChangeList.Add(c);
2135                             }
2136                             break;
2137 
2138                         case DataRowAction.Commit:
2139                             break;
2140                     }
2141                 }
2142             }
2143             finally
2144             {
2145                 _ignoreXmlEvents = false;
2146                 IsFoliationEnabled = wasFoliationEnabled;
2147             }
2148         }
2149 
OnDataSetPropertyChanging(object oDataSet, PropertyChangedEventArgs args)2150         private void OnDataSetPropertyChanging(object oDataSet, PropertyChangedEventArgs args)
2151         {
2152             if (args.PropertyName == "DataSetName")
2153                 throw new InvalidOperationException(SR.DataDom_DataSetNameChange);
2154         }
OnColumnPropertyChanging(object oColumn, PropertyChangedEventArgs args)2155         private void OnColumnPropertyChanging(object oColumn, PropertyChangedEventArgs args)
2156         {
2157             if (args.PropertyName == "ColumnName")
2158                 throw new InvalidOperationException(SR.DataDom_ColumnNameChange);
2159             if (args.PropertyName == "Namespace")
2160                 throw new InvalidOperationException(SR.DataDom_ColumnNamespaceChange);
2161             if (args.PropertyName == "ColumnMapping")
2162                 throw new InvalidOperationException(SR.DataDom_ColumnMappingChange);
2163         }
OnTablePropertyChanging(object oTable, PropertyChangedEventArgs args)2164         private void OnTablePropertyChanging(object oTable, PropertyChangedEventArgs args)
2165         {
2166             if (args.PropertyName == "TableName")
2167                 throw new InvalidOperationException(SR.DataDom_TableNameChange);
2168             if (args.PropertyName == "Namespace")
2169                 throw new InvalidOperationException(SR.DataDom_TableNamespaceChange);
2170         }
OnTableColumnsChanging(object oColumnsCollection, CollectionChangeEventArgs args)2171         private void OnTableColumnsChanging(object oColumnsCollection, CollectionChangeEventArgs args)
2172         {
2173             // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
2174             // args.Element is one of either the column (for Add and Remove actions or null, if the entire colection of columns is changing)
2175 
2176             // Disallow changing the columns collection (since we are subscribed only in populated mode, we allow changes in any state but non-populated mode)
2177             throw new InvalidOperationException(SR.DataDom_TableColumnsChange);
2178         }
2179 
OnDataSetTablesChanging(object oTablesCollection, CollectionChangeEventArgs args)2180         private void OnDataSetTablesChanging(object oTablesCollection, CollectionChangeEventArgs args)
2181         {
2182             // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
2183             // args.Element is a table
2184 
2185             // Disallow changing the tables collection (since we are subscribed only in populated mode, we allow changes in any state but non-populated mode)
2186             throw new InvalidOperationException(SR.DataDom_DataSetTablesChange);
2187         }
2188 
OnDataSetRelationsChanging(object oRelationsCollection, CollectionChangeEventArgs args)2189         private void OnDataSetRelationsChanging(object oRelationsCollection, CollectionChangeEventArgs args)
2190         {
2191             // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
2192             // args.Element is a DataRelation
2193 
2194             // Disallow changing the tables collection if there is data loaded and there are nested relationship that are added/refreshed
2195             DataRelation rel = (DataRelation)(args.Element);
2196             if (rel != null && rel.Nested)
2197                 throw new InvalidOperationException(SR.DataDom_DataSetNestedRelationsChange);
2198 
2199             // If Add and Remove, we should already been throwing if .Nested == false
2200             Debug.Assert(!(args.Action == CollectionChangeAction.Add || args.Action == CollectionChangeAction.Remove) || rel.Nested == false);
2201             if (args.Action == CollectionChangeAction.Refresh)
2202             {
2203                 foreach (DataRelation relTemp in (DataRelationCollection)oRelationsCollection)
2204                 {
2205                     if (relTemp.Nested)
2206                     {
2207                         throw new InvalidOperationException(SR.DataDom_DataSetNestedRelationsChange);
2208                     }
2209                 }
2210             }
2211         }
2212 
OnRelationPropertyChanging(object oRelationsCollection, PropertyChangedEventArgs args)2213         private void OnRelationPropertyChanging(object oRelationsCollection, PropertyChangedEventArgs args)
2214         {
2215             if (args.PropertyName == "Nested")
2216                 throw new InvalidOperationException(SR.DataDom_DataSetNestedRelationsChange);
2217         }
2218 
OnUndeleteRow(DataRow row, XmlElement rowElement)2219         private void OnUndeleteRow(DataRow row, XmlElement rowElement)
2220         {
2221             XmlNode refRow;
2222             XmlElement parent;
2223 
2224             // make certain we weren't place somewhere else.
2225             if (rowElement.ParentNode != null)
2226                 rowElement.ParentNode.RemoveChild(rowElement);
2227 
2228             // Find the parent of RowNode to be inserted
2229             DataRow parentRowInRelation = GetNestedParent(row);
2230             if (parentRowInRelation == null)
2231             {
2232                 parent = EnsureNonRowDocumentElement();
2233             }
2234             else
2235                 parent = GetElementFromRow(parentRowInRelation);
2236 
2237             if ((refRow = GetRowInsertBeforeLocation(row, rowElement, parent)) != null)
2238                 parent.InsertBefore(rowElement, refRow);
2239             else
2240                 parent.AppendChild(rowElement);
2241 
2242             FixNestedChildren(row, rowElement);
2243         }
2244 
2245         // Promote the rowElemChild node/region after prevSibling node (as the next sibling)
PromoteChild(XmlNode child, XmlNode prevSibling)2246         private void PromoteChild(XmlNode child, XmlNode prevSibling)
2247         {
2248             // It makes no sense to move rowElemChild on the same level
2249             Debug.Assert(child.ParentNode != prevSibling.ParentNode);
2250             // prevSibling must have a parent, since we want to add a sibling to it
2251             Debug.Assert(prevSibling.ParentNode != null);
2252             Debug.Assert(IsFoliationEnabled == false);
2253             Debug.Assert(IgnoreXmlEvents == true);
2254             // Should not insert after docElem node
2255             Debug.Assert(prevSibling != DocumentElement);
2256 
2257             if (child.ParentNode != null)
2258                 child.ParentNode.RemoveChild(child);
2259 
2260             Debug.Assert(child.ParentNode == null);
2261             prevSibling.ParentNode.InsertAfter(child, prevSibling);
2262         }
2263 
2264         // Promote child regions under parent as next siblings of parent
PromoteInnerRegions(XmlNode parent)2265         private void PromoteInnerRegions(XmlNode parent)
2266         {
2267             Debug.Assert(parent != null);
2268             Debug.Assert(parent.NodeType != XmlNodeType.Attribute);   // We need to get get the grand-parent region
2269             Debug.Assert(parent != DocumentElement);                  // We cannot promote children of the DocumentElement
2270 
2271             XmlNode prevSibling = parent;
2272             XmlBoundElement parentRegionRowElem;
2273             _mapper.GetRegion(parent.ParentNode, out parentRegionRowElem);
2274 
2275             TreeIterator iter = new TreeIterator(parent);
2276             bool fMore = iter.NextRowElement();
2277             while (fMore)
2278             {
2279                 Debug.Assert(iter.CurrentNode is XmlBoundElement && ((XmlBoundElement)(iter.CurrentNode)).Row != null);
2280                 XmlBoundElement rowElemChild = (XmlBoundElement)(iter.CurrentNode);
2281                 fMore = iter.NextRightRowElement();
2282                 PromoteChild(rowElemChild, prevSibling);
2283                 SetNestedParentRegion(rowElemChild, parentRegionRowElem);
2284             }
2285         }
2286 
PromoteNonValueChildren(XmlNode parent)2287         private void PromoteNonValueChildren(XmlNode parent)
2288         {
2289             Debug.Assert(parent != null);
2290             XmlNode prevSibling = parent;
2291             XmlNode child = parent.FirstChild;
2292             bool bTextLikeNode = true;
2293             XmlNode nextSibling = null;
2294             while (child != null)
2295             {
2296                 nextSibling = child.NextSibling;
2297                 if (!bTextLikeNode || !IsTextLikeNode(child))
2298                 {
2299                     bTextLikeNode = false;
2300                     nextSibling = child.NextSibling;
2301                     PromoteChild(child, prevSibling);
2302                     prevSibling = child;
2303                 }
2304                 child = nextSibling;
2305             }
2306         }
2307 
RemoveInitialTextNodes(XmlNode node)2308         private void RemoveInitialTextNodes(XmlNode node)
2309         {
2310             while (node != null && IsTextLikeNode(node))
2311             {
2312                 XmlNode sibling = node.NextSibling;
2313                 node.ParentNode.RemoveChild(node);
2314                 node = sibling;
2315             }
2316         }
2317 
ReplaceInitialChildText(XmlNode parent, string value)2318         private void ReplaceInitialChildText(XmlNode parent, string value)
2319         {
2320             XmlNode n = parent.FirstChild;
2321 
2322             // don't consider whitespace when replacing initial text
2323             while (n != null && n.NodeType == XmlNodeType.Whitespace)
2324                 n = n.NextSibling;
2325 
2326             if (n != null)
2327             {
2328                 if (n.NodeType == XmlNodeType.Text)
2329                     n.Value = value;
2330                 else
2331                     n = parent.InsertBefore(CreateTextNode(value), n);
2332                 RemoveInitialTextNodes(n.NextSibling);
2333             }
2334             else
2335             {
2336                 parent.AppendChild(CreateTextNode(value));
2337             }
2338         }
2339 
SafeFirstChild(XmlNode n)2340         internal XmlNode SafeFirstChild(XmlNode n)
2341         {
2342             XmlBoundElement be = n as XmlBoundElement;
2343             if (be != null)
2344                 return be.SafeFirstChild;
2345             else
2346                 //other type of node should be already foliated.
2347                 return n.FirstChild;
2348         }
2349 
SafeNextSibling(XmlNode n)2350         internal XmlNode SafeNextSibling(XmlNode n)
2351         {
2352             XmlBoundElement be = n as XmlBoundElement;
2353             if (be != null)
2354                 return be.SafeNextSibling;
2355             else
2356                 //other type of node should be already foliated.
2357                 return n.NextSibling;
2358         }
2359 
SafePreviousSibling(XmlNode n)2360         internal XmlNode SafePreviousSibling(XmlNode n)
2361         {
2362             XmlBoundElement be = n as XmlBoundElement;
2363             if (be != null)
2364                 return be.SafePreviousSibling;
2365             else
2366                 //other type of node should be already foliated.
2367                 return n.PreviousSibling;
2368         }
2369 
SetRowValueToNull(DataRow row, DataColumn col)2370         internal static void SetRowValueToNull(DataRow row, DataColumn col)
2371         {
2372             Debug.Assert(col.ColumnMapping != MappingType.Hidden);
2373 
2374             if (!(row.IsNull(col)))
2375             {
2376                 row[col] = DBNull.Value;
2377             }
2378         }
2379 
SetRowValueFromXmlText(DataRow row, DataColumn col, string xmlText)2380         internal static void SetRowValueFromXmlText(DataRow row, DataColumn col, string xmlText)
2381         {
2382             Debug.Assert(xmlText != null);
2383             Debug.Assert(row.Table.DataSet.EnforceConstraints == false);
2384             object oVal;
2385             try
2386             {
2387                 oVal = col.ConvertXmlToObject(xmlText);
2388                 // This func does not set the field value to null - call SetRowValueToNull in order to do so
2389                 Debug.Assert(oVal != null && !(oVal is DBNull));
2390             }
2391             catch (Exception e) when (Data.Common.ADP.IsCatchableExceptionType(e))
2392             {
2393                 // Catch data-type errors and set ROM to Unspecified value
2394                 SetRowValueToNull(row, col);
2395                 return;
2396             }
2397 
2398             if (!oVal.Equals(row[col]))
2399                 row[col] = oVal;
2400         }
2401 
SynchronizeRowFromRowElement(XmlBoundElement rowElement)2402         private void SynchronizeRowFromRowElement(XmlBoundElement rowElement)
2403         {
2404             SynchronizeRowFromRowElement(rowElement, null);
2405         }
2406         // Sync row fields w/ values from rowElem region.
2407         // If rowElemList is != null, all subregions of rowElem are appended to it.
SynchronizeRowFromRowElement(XmlBoundElement rowElement, ArrayList rowElemList)2408         private void SynchronizeRowFromRowElement(XmlBoundElement rowElement, ArrayList rowElemList)
2409         {
2410             DataRow row = rowElement.Row;
2411             Debug.Assert(row != null);
2412 
2413             // No synchronization needed for deleted rows
2414             if (row.RowState == DataRowState.Deleted)
2415                 return;
2416 
2417             row.BeginEdit();
2418 #if DEBUG
2419             try
2420             {
2421 #endif
2422                 SynchronizeRowFromRowElementEx(rowElement, rowElemList);
2423 #if DEBUG
2424             }
2425             catch
2426             {
2427                 // We should not get any exceptions because we always handle data-type conversion
2428                 Debug.Assert(false);
2429                 throw;
2430             }
2431 #endif
2432 #if DEBUG
2433             try
2434             {
2435 #endif
2436                 row.EndEdit();
2437 #if DEBUG
2438             }
2439             catch
2440             {
2441                 // We should not get any exceptions because DataSet.EnforceConstraints should be always off
2442                 Debug.Assert(false);
2443                 throw;
2444             }
2445 #endif
2446         }
SynchronizeRowFromRowElementEx(XmlBoundElement rowElement, ArrayList rowElemList)2447         private void SynchronizeRowFromRowElementEx(XmlBoundElement rowElement, ArrayList rowElemList)
2448         {
2449             Debug.Assert(rowElement != null);
2450             Debug.Assert(rowElement.Row != null);
2451             Debug.Assert(DataSet.EnforceConstraints == false);
2452 
2453             DataRow row = rowElement.Row;
2454             Debug.Assert(row != null);
2455             DataTable table = row.Table;
2456 
2457             Hashtable foundColumns = new Hashtable();
2458             string xsi_attrVal = string.Empty;
2459 
2460             RegionIterator iter = new RegionIterator(rowElement);
2461             bool fMore;
2462             // If present, fill up the TextOnly column
2463             DataColumn column = GetTextOnlyColumn(row);
2464             if (column != null)
2465             {
2466                 foundColumns[column] = column;
2467                 string value;
2468                 fMore = iter.NextInitialTextLikeNodes(out value);
2469                 if (value.Length == 0 && (((xsi_attrVal = rowElement.GetAttribute(XSI_NIL)) == "1") || xsi_attrVal == "true"))
2470                     row[column] = DBNull.Value;
2471                 else
2472                     SetRowValueFromXmlText(row, column, value);
2473             }
2474             else
2475                 fMore = iter.Next();
2476 
2477             // Fill up the columns mapped to an element
2478             while (fMore)
2479             {
2480                 XmlElement e = iter.CurrentNode as XmlElement;
2481                 if (e == null)
2482                 {
2483                     fMore = iter.Next();
2484                     continue;
2485                 }
2486 
2487                 XmlBoundElement be = e as XmlBoundElement;
2488                 if (be != null && be.Row != null)
2489                 {
2490                     if (rowElemList != null)
2491                         rowElemList.Add(e);
2492                     // Skip over sub-regions
2493                     fMore = iter.NextRight();
2494                     continue;
2495                 }
2496 
2497                 DataColumn c = _mapper.GetColumnSchemaForNode(rowElement, e);
2498                 if (c != null)
2499                 {
2500                     Debug.Assert(c.Table == row.Table);
2501                     if (foundColumns[c] == null)
2502                     {
2503                         foundColumns[c] = c;
2504                         string value;
2505                         fMore = iter.NextInitialTextLikeNodes(out value);
2506                         if (value.Length == 0 && (((xsi_attrVal = e.GetAttribute(XSI_NIL)) == "1") || xsi_attrVal == "true"))
2507                             row[c] = DBNull.Value;
2508                         else
2509                             SetRowValueFromXmlText(row, c, value);
2510                         continue;
2511                     }
2512                 }
2513 
2514                 fMore = iter.Next();
2515             }
2516 
2517             //
2518             // Walk the attributes to find attributes that map to columns.
2519             //
2520             foreach (XmlAttribute attr in rowElement.Attributes)
2521             {
2522                 DataColumn c = _mapper.GetColumnSchemaForNode(rowElement, attr);
2523 
2524                 if (c != null)
2525                 {
2526                     if (foundColumns[c] == null)
2527                     {
2528                         foundColumns[c] = c;
2529                         SetRowValueFromXmlText(row, c, attr.Value);
2530                     }
2531                 }
2532             }
2533 
2534             // Null all columns values that aren't represented in the tree
2535             foreach (DataColumn c in row.Table.Columns)
2536             {
2537                 if (foundColumns[c] == null && !IsNotMapped(c))
2538                 {
2539                     if (!c.AutoIncrement)
2540                         SetRowValueToNull(row, c);
2541                     else
2542                         c.Init(row._tempRecord);
2543                 }
2544             }
2545         }
2546 
UpdateAllColumns(DataRow row, XmlBoundElement rowElement)2547         private void UpdateAllColumns(DataRow row, XmlBoundElement rowElement)
2548         {
2549             foreach (DataColumn c in row.Table.Columns)
2550             {
2551                 OnColumnValueChanged(row, c, rowElement);
2552             }
2553         }
2554 
2555         /// <summary>
2556         /// Initializes a new instance of the XmlDataDocument class.
2557         /// </summary>
XmlDataDocument()2558         public XmlDataDocument() : base(new XmlDataImplementation())
2559         {
2560             Init();
2561             AttachDataSet(new DataSet());
2562             _dataSet.EnforceConstraints = false;
2563         }
2564 
2565         /// <summary>
2566         /// Initializes a new instance of the XmlDataDocument class with the specified
2567         /// DataSet.
2568         /// </summary>
XmlDataDocument(DataSet dataset)2569         public XmlDataDocument(DataSet dataset) : base(new XmlDataImplementation())
2570         {
2571             Init(dataset);
2572         }
2573 
XmlDataDocument(XmlImplementation imp)2574         internal XmlDataDocument(XmlImplementation imp) : base(imp)
2575         {
2576         }
2577 
Init()2578         private void Init()
2579         {
2580             _pointers = new Hashtable();
2581             _countAddPointer = 0;
2582             _columnChangeList = new ArrayList();
2583             _ignoreDataSetEvents = false;
2584             _isFoliationEnabled = true;
2585             _optimizeStorage = true;
2586             _fDataRowCreatedSpecial = false;
2587             _autoFoliationState = ElementState.StrongFoliation;
2588             _fAssociateDataRow = true; //this needs to be true for newly created elements should have associated datarows
2589             _mapper = new DataSetMapper();
2590             _foliationLock = new object();
2591             _ignoreXmlEvents = true;
2592             _attrXml = CreateAttribute("xmlns", "xml", XPathNodePointer.StrReservedXmlns);
2593             _attrXml.Value = XPathNodePointer.StrReservedXml;
2594             _ignoreXmlEvents = false;
2595         }
2596 
Init(DataSet ds)2597         private void Init(DataSet ds)
2598         {
2599             if (ds == null)
2600                 throw new ArgumentException(SR.DataDom_DataSetNull);
2601             Init();
2602             if (ds.FBoundToDocument)
2603                 throw new ArgumentException(SR.DataDom_MultipleDataSet);
2604             ds.FBoundToDocument = true;
2605             _dataSet = ds;
2606             Bind(true);
2607         }
2608 
IsConnected(XmlNode node)2609         private bool IsConnected(XmlNode node)
2610         {
2611             while (true)
2612             {
2613                 if (node == null)
2614                     return false;
2615                 if (node == this)
2616                     return true;
2617 
2618                 XmlAttribute attr = node as XmlAttribute;
2619                 if (attr != null)
2620                     node = attr.OwnerElement;
2621                 else
2622                     node = node.ParentNode;
2623             }
2624         }
IsRowLive(DataRow row)2625         private bool IsRowLive(DataRow row)
2626         {
2627             return (row.RowState & (DataRowState.Added | DataRowState.Unchanged | DataRowState.Modified)) != 0;
2628         }
SetNestedParentRow(DataRow childRow, DataRow parentRow)2629         private static void SetNestedParentRow(DataRow childRow, DataRow parentRow)
2630         {
2631             DataRelation rel = GetNestedParentRelation(childRow);
2632             //we should not set this row's parentRow if the table doesn't match.
2633             if (rel != null)
2634             {
2635                 if (parentRow == null || rel.ParentKey.Table != parentRow.Table)
2636                     childRow.SetParentRow(null, rel);
2637                 else
2638                     childRow.SetParentRow(parentRow, rel);
2639             }
2640         }
2641 
2642         // A node (node) was inserted into the main tree (connected) from oldParent==null state
OnNodeInsertedInTree(XmlNode node)2643         private void OnNodeInsertedInTree(XmlNode node)
2644         {
2645             XmlBoundElement be;
2646             ArrayList rowElemList = new ArrayList();
2647             if (_mapper.GetRegion(node, out be))
2648             {
2649                 if (be == node)
2650                 {
2651                     OnRowElementInsertedInTree(be, rowElemList);
2652                 }
2653                 else
2654                 {
2655                     OnNonRowElementInsertedInTree(node, be, rowElemList);
2656                 }
2657             }
2658             else
2659             {
2660                 // We only need to sync the embedded sub-regions
2661                 TreeIterator iter = new TreeIterator(node);
2662                 for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement())
2663                     rowElemList.Add(iter.CurrentNode);
2664             }
2665 
2666             // Process subregions, so they make transition from disconnected to connected tree
2667             while (rowElemList.Count > 0)
2668             {
2669                 Debug.Assert(rowElemList[0] != null && rowElemList[0] is XmlBoundElement);
2670                 XmlBoundElement subRowElem = (XmlBoundElement)(rowElemList[0]);
2671                 rowElemList.RemoveAt(0);
2672                 // Expect rowElem to have a DataTable schema, since it is a sub-region
2673                 Debug.Assert(subRowElem != null);
2674                 OnRowElementInsertedInTree(subRowElem, rowElemList);
2675             }
2676 
2677             // Assert that all sub-regions are assoc w/ "live" rows
2678             AssertLiveRows(node);
2679         }
2680         // "node" was inserting into a disconnected tree from oldParent==null state
OnNodeInsertedInFragment(XmlNode node)2681         private void OnNodeInsertedInFragment(XmlNode node)
2682         {
2683             XmlBoundElement be;
2684             if (_mapper.GetRegion(node, out be))
2685             {
2686                 if (be == node)
2687                 {
2688                     Debug.Assert(!IsRowLive(be.Row));
2689                     SetNestedParentRegion(be);
2690                 }
2691                 else
2692                 {
2693                     ArrayList rowElemList = new ArrayList();
2694                     OnNonRowElementInsertedInFragment(node, be, rowElemList);
2695                     // Set nested parent for the 1st level subregions (they should already be associated w/ Deleted or Detached rows)
2696                     while (rowElemList.Count > 0)
2697                     {
2698                         Debug.Assert(rowElemList[0] != null && rowElemList[0] is XmlBoundElement);
2699                         XmlBoundElement subRowElem = (XmlBoundElement)(rowElemList[0]);
2700                         rowElemList.RemoveAt(0);
2701                         SetNestedParentRegion(subRowElem, be);
2702                     }
2703                 }
2704 
2705                 // Check to make sure all sub-regions are disconnected
2706                 AssertNonLiveRows(node);
2707 
2708                 return;
2709             }
2710 
2711             // Nothing to do, since the node belongs to no region
2712 
2713             // Check to make sure all sub-regions are disconnected
2714             AssertNonLiveRows(node);
2715         }
2716 
2717         // A row-elem was inserted into the connected tree (connected) from oldParent==null state
OnRowElementInsertedInTree(XmlBoundElement rowElem, ArrayList rowElemList)2718         private void OnRowElementInsertedInTree(XmlBoundElement rowElem, ArrayList rowElemList)
2719         {
2720             Debug.Assert(rowElem.Row != null);
2721 
2722             DataRow row = rowElem.Row;
2723             DataRowState rowState = row.RowState;
2724 
2725             switch (rowState)
2726             {
2727                 case DataRowState.Detached:
2728 #if DEBUG
2729                     try
2730                     {
2731                         Debug.Assert(row.Table.DataSet.EnforceConstraints == false);
2732 #endif
2733                         row.Table.Rows.Add(row);
2734                         SetNestedParentRegion(rowElem);
2735 #if DEBUG
2736                     }
2737                     catch
2738                     {
2739                         // We should not get any exceptions here
2740                         Debug.Assert(false);
2741                         throw;
2742                     }
2743 #endif
2744                     // Add all sub-regions to the list if the caller needs this
2745                     if (rowElemList != null)
2746                     {
2747                         RegionIterator iter = new RegionIterator(rowElem);
2748                         for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement())
2749                             rowElemList.Add(iter.CurrentNode);
2750                     }
2751                     break;
2752                 case DataRowState.Deleted:
2753 #if DEBUG
2754                     try
2755                     {
2756                         Debug.Assert(row.Table.DataSet.EnforceConstraints == false);
2757 #endif
2758                         // Change the row status to be alive (unchanged)
2759                         row.RejectChanges();
2760                         // Set ROM from XML
2761                         SynchronizeRowFromRowElement(rowElem, rowElemList);
2762                         // Set nested parent data row according to where is the row positioned in the tree
2763                         SetNestedParentRegion(rowElem);
2764 #if DEBUG
2765                     }
2766                     catch
2767                     {
2768                         // We should not get any exceptions here
2769                         Debug.Assert(false);
2770                         throw;
2771                     }
2772 #endif
2773                     break;
2774                 default:
2775                     // Handle your case above
2776                     Debug.Assert(false);
2777                     break;
2778             }
2779             Debug.Assert(IsRowLive(rowElem.Row));
2780         }
2781 
2782         // Disconnect the DataRow associated w/ the rowElem region
EnsureDisconnectedDataRow(XmlBoundElement rowElem)2783         private void EnsureDisconnectedDataRow(XmlBoundElement rowElem)
2784         {
2785             Debug.Assert(rowElem.Row != null);
2786 
2787             DataRow row = rowElem.Row;
2788             DataRowState rowState = row.RowState;
2789 
2790             switch (rowState)
2791             {
2792                 case DataRowState.Detached:
2793 #if DEBUG
2794                     try
2795                     {
2796                         Debug.Assert(row.Table.DataSet.EnforceConstraints == false);
2797 #endif
2798                         SetNestedParentRegion(rowElem);
2799 #if DEBUG
2800                     }
2801                     catch
2802                     {
2803                         // We should not get any exceptions here
2804                         Debug.Assert(false);
2805                         throw;
2806                     }
2807 #endif
2808                     break;
2809 
2810                 case DataRowState.Deleted:
2811                     // Nothing to do: moving a region associated w/ a deleted row to another disconnected tree is a NO-OP.
2812                     break;
2813 
2814                 case DataRowState.Unchanged:
2815                 case DataRowState.Modified:
2816                     EnsureFoliation(rowElem, ElementState.WeakFoliation);
2817                     row.Delete();
2818                     break;
2819 
2820                 case DataRowState.Added:
2821                     EnsureFoliation(rowElem, ElementState.WeakFoliation);
2822                     row.Delete();
2823                     SetNestedParentRegion(rowElem);
2824                     break;
2825 
2826                 default:
2827                     // Handle your case above
2828                     Debug.Assert(false);
2829                     break;
2830             }
2831 
2832             Debug.Assert(!IsRowLive(rowElem.Row));
2833         }
2834 
2835 
2836         // A non-row-elem was inserted into the connected tree (connected) from oldParent==null state
OnNonRowElementInsertedInTree(XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList)2837         private void OnNonRowElementInsertedInTree(XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList)
2838         {
2839             // non-row-elem is beeing inserted
2840             DataRow row = rowElement.Row;
2841             // Region should already have an associated data row (otherwise how was the original row-elem inserted ?)
2842             Debug.Assert(row != null);
2843             SynchronizeRowFromRowElement(rowElement);
2844             if (rowElemList != null)
2845             {
2846                 TreeIterator iter = new TreeIterator(node);
2847                 for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement())
2848                     rowElemList.Add(iter.CurrentNode);
2849             }
2850         }
2851 
2852         // A non-row-elem was inserted into disconnected tree (fragment) from oldParent==null state (i.e. was disconnected)
OnNonRowElementInsertedInFragment(XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList)2853         private void OnNonRowElementInsertedInFragment(XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList)
2854         {
2855             // non-row-elem is beeing inserted
2856             DataRow row = rowElement.Row;
2857             // Region should already have an associated data row (otherwise how was the original row-elem inserted ?)
2858             Debug.Assert(row != null);
2859             // Since oldParent == null, the only 2 row states should have been Detached or Deleted
2860             Debug.Assert(row.RowState == DataRowState.Detached || row.RowState == DataRowState.Deleted);
2861 
2862             if (row.RowState == DataRowState.Detached)
2863                 SynchronizeRowFromRowElementEx(rowElement, rowElemList);
2864             // Nothing to do if the row is deleted (there is no sync-ing from XML to ROM for deleted rows)
2865         }
2866 
SetNestedParentRegion(XmlBoundElement childRowElem)2867         private void SetNestedParentRegion(XmlBoundElement childRowElem)
2868         {
2869             Debug.Assert(childRowElem.Row != null);
2870 
2871             XmlBoundElement parentRowElem;
2872             _mapper.GetRegion(childRowElem.ParentNode, out parentRowElem);
2873             SetNestedParentRegion(childRowElem, parentRowElem);
2874         }
SetNestedParentRegion(XmlBoundElement childRowElem, XmlBoundElement parentRowElem)2875         private void SetNestedParentRegion(XmlBoundElement childRowElem, XmlBoundElement parentRowElem)
2876         {
2877             DataRow childRow = childRowElem.Row;
2878             if (parentRowElem == null)
2879             {
2880                 SetNestedParentRow(childRow, null);
2881                 return;
2882             }
2883 
2884             DataRow parentRow = parentRowElem.Row;
2885             Debug.Assert(parentRow != null);
2886             // We should set it only if there is a nested relationship between this child and parent regions
2887             DataRelation[] relations = childRow.Table.NestedParentRelations;
2888             if (relations.Length != 0 && relations[0].ParentTable == parentRow.Table) // just backward compatable
2889             {
2890                 SetNestedParentRow(childRow, parentRow);
2891             }
2892             else
2893             {
2894                 SetNestedParentRow(childRow, null);
2895             }
2896         }
2897 
IsTextNode(XmlNodeType nt)2898         internal static bool IsTextNode(XmlNodeType nt)
2899         {
2900             switch (nt)
2901             {
2902                 case XmlNodeType.Text:
2903                 case XmlNodeType.CDATA:
2904                 case XmlNodeType.Whitespace:
2905                 case XmlNodeType.SignificantWhitespace:
2906                     return true;
2907                 default:
2908                     return false;
2909             }
2910         }
2911 
2912         /*
2913         internal static bool IsWhiteSpace(char ch) {
2914             switch ( ch ) {
2915                 case '\u0009' :
2916                 case '\u000a' :
2917                 case '\u000d' :
2918                 case '\u0020' :
2919                     return true;
2920                 default :
2921                     return false;
2922             }
2923         }
2924 
2925         internal static bool IsOnlyWhitespace( string str ) {
2926             if (str != null) {
2927                 for (int index = 0; index < str.Length; index ++) {
2928                     if (! IsWhiteSpace(str[index]))
2929                         return false;
2930                 }
2931             }
2932             return true;
2933         }
2934         */
2935 
CreateNavigator(XmlNode node)2936         protected override XPathNavigator CreateNavigator(XmlNode node)
2937         {
2938             Debug.Assert(node.OwnerDocument == this || node == this);
2939             if (XPathNodePointer.s_xmlNodeType_To_XpathNodeType_Map[(int)(node.NodeType)] == -1)
2940                 return null;
2941             if (IsTextNode(node.NodeType))
2942             {
2943                 XmlNode parent = node.ParentNode;
2944                 if (parent != null && parent.NodeType == XmlNodeType.Attribute)
2945                     return null;
2946                 else
2947                 {
2948 #if DEBUG
2949                     //if current node is a text node, its parent node has to be foliated
2950                     XmlBoundElement be = node.ParentNode as XmlBoundElement;
2951                     if (be != null)
2952                         Debug.Assert(be.IsFoliated);
2953 #endif
2954                     XmlNode prevSib = node.PreviousSibling;
2955                     while (prevSib != null && IsTextNode(prevSib.NodeType))
2956                     {
2957                         node = prevSib;
2958                         prevSib = SafePreviousSibling(node);
2959                     }
2960                 }
2961             }
2962             return new DataDocumentXPathNavigator(this, node);
2963         }
2964 
2965         [System.Diagnostics.Conditional("DEBUG")]
AssertLiveRows(XmlNode node)2966         private void AssertLiveRows(XmlNode node)
2967         {
2968             bool wasFoliationEnabled = IsFoliationEnabled;
2969             IsFoliationEnabled = false;
2970             try
2971             {
2972                 XmlBoundElement rowElement = node as XmlBoundElement;
2973                 if (rowElement != null && rowElement.Row != null)
2974                     Debug.Assert(IsRowLive(rowElement.Row));
2975                 TreeIterator iter = new TreeIterator(node);
2976                 for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement())
2977                 {
2978                     rowElement = iter.CurrentNode as XmlBoundElement;
2979                     Debug.Assert(rowElement.Row != null);
2980                     Debug.Assert(IsRowLive(rowElement.Row));
2981                 }
2982             }
2983             finally
2984             {
2985                 IsFoliationEnabled = wasFoliationEnabled;
2986             }
2987         }
2988         [System.Diagnostics.Conditional("DEBUG")]
AssertNonLiveRows(XmlNode node)2989         private void AssertNonLiveRows(XmlNode node)
2990         {
2991             bool wasFoliationEnabled = IsFoliationEnabled;
2992             IsFoliationEnabled = false;
2993             try
2994             {
2995                 XmlBoundElement rowElement = node as XmlBoundElement;
2996                 if (rowElement != null && rowElement.Row != null)
2997                     Debug.Assert(!IsRowLive(rowElement.Row));
2998                 TreeIterator iter = new TreeIterator(node);
2999                 for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement())
3000                 {
3001                     rowElement = iter.CurrentNode as XmlBoundElement;
3002                     Debug.Assert(rowElement.Row != null);
3003                     Debug.Assert(!IsRowLive(rowElement.Row));
3004                 }
3005             }
3006             finally
3007             {
3008                 IsFoliationEnabled = wasFoliationEnabled;
3009             }
3010         }
3011 
GetElementById(string elemId)3012         public override XmlElement GetElementById(string elemId)
3013         {
3014             throw new NotSupportedException(SR.DataDom_NotSupport_GetElementById);
3015         }
GetElementsByTagName(string name)3016         public override XmlNodeList GetElementsByTagName(string name)
3017         {
3018             // Retrieving nodes from the returned nodelist may cause foliation which causes new nodes to be created,
3019             // so the System.Xml iterator will throw if this happens during iteration. To avoid this, foliate everything
3020             // before iteration, so iteration will not cause foliation (and as a result of this, creation of new nodes).
3021             XmlNodeList tempNodeList = base.GetElementsByTagName(name);
3022 
3023             int tempint = tempNodeList.Count;
3024             return tempNodeList;
3025         }
3026 
3027         //  after adding Namespace support foir datatable, DataSet does not guarantee that infered tabels would be in the same sequence as they rae in XML, because
3028         //  of Namespace. if a table is in different namespace than its children and DataSet, that table would efinetely be added to DataSet after its children. Its By Design
3029         // so in order to maintain backward compatability, we reorder the copy of the datatable collection and use it
OrderTables(DataSet ds)3030         private DataTable[] OrderTables(DataSet ds)
3031         {
3032             DataTable[] retValue = null;
3033             if (ds == null || ds.Tables.Count == 0)
3034             {
3035                 retValue = Array.Empty<DataTable>();
3036             }
3037             else if (TablesAreOrdered(ds))
3038             {
3039                 retValue = new DataTable[ds.Tables.Count];
3040                 ds.Tables.CopyTo(retValue, 0);
3041                 // XDD assumes PArent table exist before its child, if it does not we won't be handle the case
3042                 // same as Everett
3043             }
3044 
3045             if (null == retValue)
3046             {
3047                 retValue = new DataTable[ds.Tables.Count];
3048                 List<DataTable> tableList = new List<DataTable>();
3049                 // first take the root tables that have no parent
3050                 foreach (DataTable dt in ds.Tables)
3051                 {
3052                     if (dt.ParentRelations.Count == 0)
3053                     {
3054                         tableList.Add(dt);
3055                     }
3056                 }
3057 
3058                 if (tableList.Count > 0)
3059                 { // if we have some  table inside;
3060                     foreach (DataTable dt in ds.Tables)
3061                     {
3062                         if (IsSelfRelatedDataTable(dt))
3063                         {
3064                             tableList.Add(dt);
3065                         }
3066                     }
3067                     for (int readPos = 0; readPos < tableList.Count; readPos++)
3068                     {
3069                         Debug.Assert(tableList[readPos] != null, "Temp Array is not supposed to reach to null");
3070                         foreach (DataRelation r in tableList[readPos].ChildRelations)
3071                         {
3072                             DataTable childTable = r.ChildTable;
3073                             if (!tableList.Contains(childTable))
3074                                 tableList.Add(childTable);
3075                         }
3076                     }
3077                     tableList.CopyTo(retValue);
3078                 }
3079                 else
3080                 {//there will not be  any in case just if we have circular relation dependency, just copy as they are in tablecollection use CopyTo of the collection
3081                     ds.Tables.CopyTo(retValue, 0);
3082                 }
3083             }
3084             return retValue;
3085         }
IsSelfRelatedDataTable(DataTable rootTable)3086         private bool IsSelfRelatedDataTable(DataTable rootTable)
3087         {
3088             List<DataTable> tableList = new List<DataTable>();
3089             bool retValue = false;
3090             foreach (DataRelation r in rootTable.ChildRelations)
3091             {
3092                 DataTable childTable = r.ChildTable;
3093                 if (childTable == rootTable)
3094                 {
3095                     retValue = true;
3096                     break;
3097                 }
3098                 else if (!tableList.Contains(childTable))
3099                 {
3100                     tableList.Add(childTable);
3101                 }
3102             }
3103             if (!retValue)
3104             {
3105                 for (int counter = 0; counter < tableList.Count; counter++)
3106                 {
3107                     foreach (DataRelation r in tableList[counter].ChildRelations)
3108                     {
3109                         DataTable childTable = r.ChildTable;
3110                         if (childTable == rootTable)
3111                         {
3112                             retValue = true;
3113                             break;
3114                         }
3115                         else if (!tableList.Contains(childTable))
3116                         {
3117                             tableList.Add(childTable);
3118                         }
3119                     }
3120                     if (retValue)
3121                     {
3122                         break;
3123                     }
3124                 }
3125             }
3126             return retValue;
3127         }
TablesAreOrdered(DataSet ds)3128         private bool TablesAreOrdered(DataSet ds)
3129         {
3130             foreach (DataTable dt in ds.Tables)
3131             {
3132                 if (dt.Namespace != ds.Namespace)
3133                 {
3134                     return false;
3135                 }
3136             }
3137             return true;
3138         }
3139     }
3140 }
3141