XmlDataDocument.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Data / System / NewXml / XmlDataDocument.cs / 1 / XmlDataDocument.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
// [....] 
//----------------------------------------------------------------------------- 
namespace System.Xml {
    using System; 
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data; 
    using System.Diagnostics;
    using System.ComponentModel; 
    using System.Xml.XPath; 

    ///  
    ///    
    ///       Represents an entire document. An XmlDataDocument can contain XML
    ///       data or relational data (DataSet).
    ///     
    /// 
    [System.Security.Permissions.HostProtectionAttribute(Synchronization=true)] 
#if WINFSInternalOnly 
    internal
#else 
    public
#endif
    class XmlDataDocument: XmlDocument {
        DataSet dataSet; 

        DataSetMapper mapper; 
        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 
                                    // that there is at most one thread in AddPointer we are OK.
        int     countAddPointer;    // Approximate count of how many times AddPointer was called since the last time we removed the unused pointer objects from pointers hashtable. 
        ArrayList columnChangeList;
        DataRowState rollbackState;

        bool fBoundToDataSet;       // true if our permanent event listeners are registered to receive DataSet events 
        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.
        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). 
        bool ignoreXmlEvents;       // true if XML events should not be processed 
        bool ignoreDataSetEvents;   // true if DataSet events should not be processed
        bool isFoliationEnabled;    // true if we should create and reveal the virtual nodes, false if we should reveal only the physical stored nodes 
        bool optimizeStorage;       // false if we should only have foilated regions.
        ElementState autoFoliationState;    // When XmlBoundElement will foliate because of memeber functions, this will contain the foliation mode: usually this is
                                    // ElementState.StrongFoliation, however when foliation occurs due to DataDocumentNavigator operations (InsertNode for example),
                                    // it it usually ElementState.WeakFoliation 
        bool fAssociateDataRow;     // if true, CreateElement will create and associate data rows w/ the newly created XmlBoundElement.
                                    // If false, then CreateElement will just create the XmlBoundElement nodes. This is usefull for Loading case, 
                                    // when CreateElement is called by DOM. 
        object foliationLock;
        internal const string XSI_NIL = "xsi:nil"; 
        internal const string XSI = "xsi";
        private bool bForceExpandEntity = false;
        internal XmlAttribute attrXml = null;
        internal bool bLoadFromDataSet = false; 
        internal bool bHasXSINIL = false;
 
        internal void AddPointer( IXmlDataVirtualNode pointer ) { 
            Debug.Assert( pointers.ContainsValue(pointer) == false );
            lock ( pointers ) { 
                countAddPointer++;
                if ( countAddPointer >= 5 ) {   // 5 is choosed to be small enough to not affect perf, but high enough so we will not scan all the time
                    ArrayList al = new ArrayList();
                    foreach( DictionaryEntry entry in pointers ) { 
                        IXmlDataVirtualNode temp = (IXmlDataVirtualNode)(entry.Value);
                        Debug.Assert( temp != null ); 
                        if ( ! temp.IsInUse() ) 
                            al.Add( temp );
                    } 
                    for (int i = 0; i < al.Count; i++ ) {
                        pointers.Remove( al[ i ] );
                    }
                    countAddPointer = 0; 
                }
                pointers[pointer] = pointer; 
            } 
        }
 
        [System.Diagnostics.Conditional("DEBUG")]
        internal void AssertPointerPresent( IXmlDataVirtualNode pointer ) {
#if DEBUG
                object val = pointers[pointer]; 
                if ( val != ( object ) pointer )
                    Debug.Assert( false ); 
#endif 
        }
        // This function attaches the DataSet to XmlDataDocument 
        // We also register a special listener (OnDataRowCreatedSpecial) to DataSet, so we know when we should setup all regular listeners (OnDataRowCreated, OnColumnChanging, etc).
        // We need to do this because of the following scenario:
        //  - XmlDataDocument doc = new XmlDataDocument();
        //  - DataSet ds = doc.DataSet;     // doc.DataSet creates a data-set, however does not sets-up the regular listeners. 
        //  - ds.ReadXmlSchema();           // since there are regular listeners in doc that track ds schema changes, doc does not know about the new tables/columns/etc
        //  - ds.ReadXmlData();             // ds is now filled, however doc has no content (since there were no listeners for the new created DataRow's) 
        // 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 
        // (we rely on the fact that DataRowCreated is a DataSet wide event, rather than a DataTable event)
        private void AttachDataSet( DataSet ds ) { 
            // You should not have already an associated dataset
            Debug.Assert( dataSet == null );
            Debug.Assert( ds != null );
            if ( ds.FBoundToDocument ) 
                throw new ArgumentException( Res.GetString(Res.DataDom_MultipleDataSet) );
            ds.FBoundToDocument = true; 
            dataSet = ds; 
            // Register the special listener to catch the first DataRow event(s)
            BindSpecialListeners(); 
        }

        // after loading, all detached DataRows are synchronized with the xml tree and inserted to their tables
        // or after setting the innerxml, synchronize the rows and if created new and detached, will be inserted. 
        internal void SyncRows( DataRow parentRow, XmlNode node, bool fAddRowsToTable) {
            XmlBoundElement be = node as XmlBoundElement; 
            if ( be != null ) { 
                DataRow r = be.Row;
                if (r!=null && be.ElementState == ElementState.Defoliated) 
                    return; //no need of syncRow

                if ( r != null ) {
                    // get all field values. 
                    SynchronizeRowFromRowElement( be );
 
                    // defoliate if possible 
                    be.ElementState = ElementState.WeakFoliation;
                    DefoliateRegion( be ); 

                    if ( parentRow != null )
                        SetNestedParentRow( r, parentRow );
                    if ( fAddRowsToTable && r.RowState == DataRowState.Detached ) 
                        r.Table.Rows.Add( r );
                    parentRow = r; 
                } 
            }
 
            // Attach all rows from children nodes
            for ( XmlNode child = node.FirstChild; child != null; child = child.NextSibling )
            	SyncRows( parentRow, child, fAddRowsToTable );
        } 

        // All detached DataRows are synchronized with the xml tree and inserted to their tables. 
        // Synchronize the rows and if created new and detached, will be inserted. 
        internal void SyncTree( XmlNode node) {
            XmlBoundElement be = null; 
            mapper.GetRegion( node, out be ) ;
            DataRow parentRow = null;
            bool fAddRowsToTable = IsConnected(node) ;
 
            if ( be != null ) {
                DataRow r = be.Row; 
                if (r!=null && be.ElementState == ElementState.Defoliated) 
                    return; //no need of syncRow
 
                if ( r != null ) {
                    // get all field values.
                    SynchronizeRowFromRowElement( be );
 
                    // defoliation will not be done on the node which is not RowElement, in case of node is externally being used
                    if ( node == be ) { 
                         // defoliate if possible 
                         be.ElementState = ElementState.WeakFoliation;
                         DefoliateRegion( be ); 
                    }

                    if ( fAddRowsToTable && r.RowState == DataRowState.Detached )
                        r.Table.Rows.Add( r ); 

                    parentRow = r; 
                } 
            }
 
            // Attach all rows from children nodes
            for ( XmlNode child = node.FirstChild; child != null; child = child.NextSibling )
            	SyncRows( parentRow, child, fAddRowsToTable );
        } 

        internal ElementState AutoFoliationState { 
            get { return this.autoFoliationState; } 
            set { this.autoFoliationState = value; }
        } 

        private void BindForLoad() {
            Debug.Assert( ignoreXmlEvents == true );
            ignoreDataSetEvents = true; 
            mapper.SetupMapping( this, dataSet );
            if ( dataSet.Tables.Count > 0 ) { 
                //at least one table 
                LoadDataSetFromTree( );
            } 
            BindListeners();
            ignoreDataSetEvents = false;
        }
 
        private void Bind( bool fLoadFromDataSet ) {
            // If we have a DocumentElement then it is illegal to call this func to load from data-set 
            Debug.Assert( DocumentElement == null || ! fLoadFromDataSet ); 

            ignoreDataSetEvents = true; 
            ignoreXmlEvents = true;

            // Do the mapping. This could be a successive mapping in case of this scenario: xd = XmlDataDocument( emptyDataSet ); xd.Load( "file.xml" );
            mapper.SetupMapping(this, dataSet); 

            if ( DocumentElement != null ) { 
                LoadDataSetFromTree(); 
                BindListeners();
            } 
            else if ( fLoadFromDataSet ) {
                this.bLoadFromDataSet = true;
                LoadTreeFromDataSet( DataSet );
                BindListeners(); 
            }
 
            ignoreDataSetEvents = false; 
            ignoreXmlEvents = false;
        } 

        internal void Bind( DataRow r, XmlBoundElement e ) {
            r.Element = e;
            e.Row = r; 
        }
 
        // Binds special listeners to catch the 1st data-row created. When the 1st DataRow is created, XmlDataDocument will automatically bind all regular listeners. 
        private void BindSpecialListeners() {
            Debug.Assert( fDataRowCreatedSpecial == false ); 
            Debug.Assert( fBoundToDataSet == false && fBoundToDocument == false );
            dataSet.DataRowCreated += new DataRowCreatedEventHandler( this.OnDataRowCreatedSpecial );
            fDataRowCreatedSpecial = true;
        } 
        private void UnBindSpecialListeners() {
            Debug.Assert( fDataRowCreatedSpecial == true ); 
            dataSet.DataRowCreated -= new DataRowCreatedEventHandler( this.OnDataRowCreatedSpecial ); 
            fDataRowCreatedSpecial = false;
        } 

        private void BindListeners() {
            BindToDocument();
            BindToDataSet(); 
        }
 
        private void BindToDataSet() { 
            // We could be already bound to DataSet in this scenario:
            //     xd = new XmlDataDocument( dataSetThatHasNoData ); xd.Load( "foo.xml" ); 
            // so we must not rebound again to it.
            if ( fBoundToDataSet ) {
                Debug.Assert( dataSet != null );
                return; 
            }
 
            // Unregister the DataRowCreatedSpecial notification 
            if ( fDataRowCreatedSpecial )
                UnBindSpecialListeners(); 

            dataSet.Tables.CollectionChanging += new CollectionChangeEventHandler( this.OnDataSetTablesChanging );
            dataSet.Relations.CollectionChanging += new CollectionChangeEventHandler( this.OnDataSetRelationsChanging );
            dataSet.DataRowCreated += new DataRowCreatedEventHandler( this.OnDataRowCreated ); 
            dataSet.PropertyChanging += new PropertyChangedEventHandler( this.OnDataSetPropertyChanging );
 
            //this is the hack for this release, should change it in the future 
            dataSet.ClearFunctionCalled += new DataSetClearEventhandler( this.OnClearCalled );
 
            if ( dataSet.Tables.Count > 0 ) {
                foreach( DataTable t in dataSet.Tables ) {
                    BindToTable( t );
                } 
            }
 
            foreach ( DataRelation rel in dataSet.Relations ) { 
                rel.PropertyChanging += new PropertyChangedEventHandler( this.OnRelationPropertyChanging );
            } 
            fBoundToDataSet = true;
        }

        private void BindToDocument() { 
            if ( !fBoundToDocument ) {
                NodeInserting +=  new XmlNodeChangedEventHandler( this.OnNodeInserting ) ; 
                NodeInserted +=  new XmlNodeChangedEventHandler( this.OnNodeInserted ) ; 
                NodeRemoving +=  new XmlNodeChangedEventHandler( this.OnNodeRemoving ) ;
                NodeRemoved +=  new XmlNodeChangedEventHandler( this.OnNodeRemoved ) ; 
                NodeChanging +=  new XmlNodeChangedEventHandler( this.OnNodeChanging ) ;
                NodeChanged +=  new XmlNodeChangedEventHandler( this.OnNodeChanged ) ;
                fBoundToDocument = true;
            } 
        }
 
        private void BindToTable( DataTable t ) { 
//            t.ColumnChanging  += new DataColumnChangeEventHandler( this.OnColumnChanging );
            t.ColumnChanged   += new DataColumnChangeEventHandler( this.OnColumnChanged ); 
            t.RowChanging     += new DataRowChangeEventHandler( this.OnRowChanging );
            t.RowChanged      += new DataRowChangeEventHandler( this.OnRowChanged );
            t.RowDeleting     += new DataRowChangeEventHandler( this.OnRowChanging);
            t.RowDeleted      += new DataRowChangeEventHandler( this.OnRowChanged ); 
            t.PropertyChanging += new PropertyChangedEventHandler( this.OnTablePropertyChanging );
            t.Columns.CollectionChanging += new CollectionChangeEventHandler( this.OnTableColumnsChanging ); 
 
            foreach( DataColumn col in t.Columns ) {
                // Hook column properties changes, so we can react properly to ROM changes. 
                col.PropertyChanging += new PropertyChangedEventHandler( this.OnColumnPropertyChanging );
            }
        }
 
        /// 
        ///     
        ///       Creates an element with the specified Prefix, LocalName, and 
        ///       NamespaceURI.
        ///     
        /// 
        public override XmlElement CreateElement( string prefix, string localName, string namespaceURI) {
            //
 
            // There are three states for the document:
            //  - 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 
            //    is created we should subscribe the permenent listeners. 
            //  - special listeners OFF, permanent listeners ON: this is when the data doc is loaded (from dataset or XML file) and synchronization takes place.
            //  - 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, 
            //    not based on listening to events.
#if DEBUG
            // Cannot have both special and permananent listeners ON
            if ( fDataRowCreatedSpecial ) 
                Debug.Assert( (fBoundToDataSet == false) && (fBoundToDocument == false) );
            // fBoundToDataSet and fBoundToDocument should have the same value 
            Debug.Assert( fBoundToDataSet ? fBoundToDocument : (! fBoundToDocument) ); 
#endif
            if ( prefix == null ) 
                prefix = String.Empty;
            if ( namespaceURI == null )
                namespaceURI = String.Empty;
 
            if ( ! fAssociateDataRow ) {
                // Loading state: create just the XmlBoundElement: the LoadTreeFromDataSet/LoadDataSetFromTree will take care of synchronization 
                return new XmlBoundElement( prefix, localName, namespaceURI, this ); 
            }
 
            // 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/
            // creation of this element
            EnsurePopulatedMode();
            Debug.Assert( fDataRowCreatedSpecial == false ); 

            // Loaded state: create a DataRow, this in turn will create and associate the XmlBoundElement, which we will return. 
            DataTable dt = mapper.SearchMatchingTableSchema( localName, namespaceURI ); 
            if ( dt != null ) {
                DataRow row = dt.CreateEmptyRow(); 
                // We need to make sure all fields are DBNull
                foreach( DataColumn col in dt.Columns ) {
                    if ( col.ColumnMapping != MappingType.Hidden )
                        SetRowValueToNull( row, col ); 
                }
                XmlBoundElement be = row.Element; 
                Debug.Assert( be != null ); 
                be.Prefix = prefix;
                return be; 
            }
            // No matching table schema for this element: just create the element
            return new XmlBoundElement( prefix, localName, namespaceURI, this );
        } 

        ///  
        ///    [To be supplied.] 
        /// 
        public override XmlEntityReference CreateEntityReference(String name) { 
            throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_EntRef ) );
        }

        ///  
        ///    Gets a DataSet that provides a relational representation of the data in this
        ///       XmlDataDocument. 
        ///  
        public DataSet DataSet {
            get { 
                return dataSet;
            }
        }
 
        private void DefoliateRegion( XmlBoundElement rowElem ) {
            // You must pass a row element (which s/b associated w/ a DataRow) 
            Debug.Assert( rowElem.Row != null ); 

            if ( !optimizeStorage ) 
                return;

            if ( rowElem.ElementState != ElementState.WeakFoliation )
                return; 

            if ( !mapper.IsRegionRadical( rowElem ) ) { 
                //Console.WriteLine("Region is not radical: "+rowElem.GetHashCode()); 
                return;
            } 

            //Console.WriteLine("Defoliating Region: " + rowElem.GetHashCode());

            bool saveIgnore = IgnoreXmlEvents; 
            IgnoreXmlEvents = true;
 
            rowElem.ElementState = ElementState.Defoliating; 

            try { 
                // drop all attributes
                rowElem.RemoveAllAttributes();

                XmlNode node = rowElem.FirstChild; 
                while ( node != null ) {
                    XmlNode next = node.NextSibling; 
 
                    XmlBoundElement be = node as XmlBoundElement;
                    if ( be != null && be.Row != null ) 
                        break;

                    // The node must be mapped to a column (since the region is radically structured)
                    Debug.Assert( mapper.GetColumnSchemaForNode( rowElem, node ) != null ); 
                    rowElem.RemoveChild( node );
 
                    node = next; 
                }
#if DEBUG 
                // All subsequent siblings must be sub-regions
                for ( ; node != null; node = node.NextSibling ) {
                    Debug.Assert( (node is XmlBoundElement) && (((XmlBoundElement)node).Row != null) );
                } 
#endif
 
                rowElem.ElementState = ElementState.Defoliated; 
            }
            finally { 
                IgnoreXmlEvents = saveIgnore;
            }
        }
 
        private XmlElement EnsureDocumentElement() {
            XmlElement docelem = DocumentElement; 
            if ( docelem == null ) { 
                string docElemName = XmlConvert.EncodeLocalName( this.DataSet.DataSetName );
                if ( docElemName == null || docElemName.Length == 0 ) 
                    docElemName = "Xml";
                string ns = this.DataSet.Namespace;
                if ( ns == null )
                    ns = String.Empty; 
                docelem = new XmlBoundElement( string.Empty, docElemName, ns, this );
                AppendChild( docelem ); 
            } 

            return docelem; 
        }
        private XmlElement EnsureNonRowDocumentElement() {
            XmlElement docElem = this.DocumentElement;
            if ( docElem == null ) 
                return EnsureDocumentElement();
 
            DataRow rowDocElem = GetRowFromElement( docElem ); 
            if ( rowDocElem == null )
                return docElem; 

            return DemoteDocumentElement();
        }
        private XmlElement DemoteDocumentElement() { 
            // Changes of Xml here should not affect ROM
            Debug.Assert( this.ignoreXmlEvents == true ); 
            // There should be no reason to call this function if docElem is not a rowElem 
            Debug.Assert( this.GetRowFromElement( this.DocumentElement ) != null );
 
            // Remove the DocumentElement and create a new one
            XmlElement oldDocElem = this.DocumentElement;
            RemoveChild( oldDocElem );
            XmlElement docElem = EnsureDocumentElement(); 
            docElem.AppendChild( oldDocElem );
            // We should have only one child now 
            Debug.Assert( docElem.LastChild == docElem.FirstChild ); 
            return docElem;
        } 
        // This function ensures that the special listeners are un-subscribed, the permanent listeners are subscribed and
        // CreateElement will attach DataRows to newly created XmlBoundElement.
        // It should be called when we have special listeners hooked and we need to change from the special-listeners mode to the
        // populated/permanenet mode where all listeners are corectly hooked up and the mapper is correctly set-up. 
        private void EnsurePopulatedMode() {
            // Unbind special listeners, bind permanent ones, setup the mapping, etc 
#if DEBUG 
            bool fDataRowCreatedSpecialOld = fDataRowCreatedSpecial;
            bool fAssociateDataRowOld = fAssociateDataRow; 
#endif
            if ( fDataRowCreatedSpecial ) {
                UnBindSpecialListeners();
                // If a special listener was ON, we should not have had an already set-up mapper or permanent listeners subscribed 
                Debug.Assert( ! mapper.IsMapped() );
                Debug.Assert( ! fBoundToDocument ); 
                Debug.Assert( ! fBoundToDataSet ); 

                mapper.SetupMapping( this, dataSet); 
                BindListeners();

                // CreateElement should now create associate DataRows w/ new XmlBoundElement nodes
                // We should do this ONLY if we switch from special listeners to permanent listeners. The reason is 
                // that DataDocumentNavigator wants to put XmlDataDocument in a batch mode, where CreateElement will just
                // create a XmlBoundElement (see DataDocumentNavigator.CloneTree) 
                fAssociateDataRow = true; 
            }
 
            Debug.Assert( fDataRowCreatedSpecial == false );
            Debug.Assert( mapper.IsMapped() );
            Debug.Assert( fBoundToDataSet && fBoundToDocument );
#if DEBUG 
            // In case we EnsurePopulatedMode was called on an already populated mode, we should NOT change fAssociateDataRow
            if ( fDataRowCreatedSpecialOld == false ) 
                Debug.Assert( fAssociateDataRowOld == fAssociateDataRow ); 
#endif
        } 

        // Move regions that are marked in ROM as nested children of row/rowElement as last children in XML fragment
        private void FixNestedChildren(DataRow row, XmlElement rowElement) {
            foreach( DataRelation dr in GetNestedChildRelations(row) ) { 
                foreach( DataRow r in row.GetChildRows(dr) ) {
                    XmlElement childElem = r.Element; 
                    // childElem can be null when we create XML from DataSet (XmlDataDocument( DataSet ) is called) and we insert rowElem of the parentRow before 
                    // we insert the rowElem of children rows.
                    if ( childElem != null ) { 
#if DEBUG
                        bool fIsChildConnected = IsConnected( childElem );
#endif
                        if ( childElem.ParentNode != rowElement ) { 
                            childElem.ParentNode.RemoveChild( childElem );
                            rowElement.AppendChild( childElem ); 
                        } 
#if DEBUG
                        // We should not have changed the connected/disconnected state of the node (since the row state did not change) 
                        Debug.Assert( fIsChildConnected == IsConnected( childElem ) );
                        Debug.Assert( IsRowLive( r ) ? IsConnected( childElem ) : ! IsConnected( childElem ) );
#endif
                    } 
                }
            } 
        } 

        // This function accepts node params that are not row-elements. In this case, calling this function is a no-op 
        internal void Foliate( XmlBoundElement node, ElementState newState ) {

            Debug.Assert( newState == ElementState.WeakFoliation || newState == ElementState.StrongFoliation );
#if DEBUG 
            // 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)
            // Do this only when we are not loading 
            if ( IsFoliationEnabled ) { 
                if( newState == ElementState.StrongFoliation && node.Row == null ) {
                    XmlBoundElement rowElem; 
                    ElementState rowElemState = ElementState.None;
                    if ( mapper.GetRegion( node, out rowElem ) ) {
                        rowElemState = rowElem.ElementState;
                        Debug.Assert( rowElemState == ElementState.StrongFoliation || rowElemState == ElementState.WeakFoliation ); 
                    }
                    // Add a no-op, so we can still debug in the assert fails 
 
#pragma warning disable 1717 // assignment to self
                    rowElemState = rowElemState; 
#pragma warning restore 1717
                }
            }
#endif 

            if ( IsFoliationEnabled ) { 
                if ( node.ElementState == ElementState.Defoliated ) { 
                    ForceFoliation( node, newState );
                } 
                else if ( node.ElementState == ElementState.WeakFoliation && newState == ElementState.StrongFoliation ) {
                    // Node must be a row-elem
                    Debug.Assert( node.Row != null );
                    node.ElementState = newState; 
                }
            } 
        } 

        private void Foliate( XmlElement element ) { 
            if ( element is XmlBoundElement )
                ((XmlBoundElement)element).Foliate( ElementState.WeakFoliation );
        }
 
        // Foliate rowElement region if there are DataPointers that points into it
        private void FoliateIfDataPointers( DataRow row, XmlElement rowElement ) { 
            if ( !IsFoliated( rowElement) && HasPointers( rowElement ) ) { 
                bool wasFoliationEnabled = IsFoliationEnabled;
                IsFoliationEnabled = true; 
                try {
                    Foliate( rowElement );
                }
                finally { 
                    IsFoliationEnabled = wasFoliationEnabled;
                } 
            } 
        }
 
        private void EnsureFoliation( XmlBoundElement rowElem, ElementState foliation ) {
            if ( rowElem.IsFoliated ) //perf reason, avoid unecessary lock.
                return;
            ForceFoliation( rowElem, foliation); 
        }
 
        private void ForceFoliation( XmlBoundElement node, ElementState newState ) { 
            lock ( this.foliationLock ) {
                if ( node.ElementState != ElementState.Defoliated ) 
                    // The region was foliated by an other thread while this thread was locked
                    return;

                // Node must be a row-elem associated w/ a non-deleted row 
                Debug.Assert( node.Row != null );
                Debug.Assert( node.Row.RowState != DataRowState.Deleted ); 
 
                node.ElementState = ElementState.Foliating;
 
                bool saveIgnore = IgnoreXmlEvents;
                IgnoreXmlEvents = true;

                try { 
                    XmlNode priorNode = null;
                    DataRow row = node.Row; 
 
                    // create new attrs & elements for row
                    // For detached rows: we are in [....] w/ temp values 
                    // For non-detached rows: we are in [....] w/ the current values
                    // For deleted rows: we never [....]
                    DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
                    foreach( DataColumn col in row.Table.Columns ) { 
                        if ( !IsNotMapped(col) ) {
                            object value = row[col, rowVersion]; 
 
                            if ( ! Convert.IsDBNull( value ) ) {
                                if ( col.ColumnMapping == MappingType.Attribute ) { 
                                    node.SetAttribute( col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml( value ) );
                                }
                                else {
                                    XmlNode newNode = null; 
                                    if ( col.ColumnMapping == MappingType.Element ) {
                                        newNode = new XmlBoundElement( string.Empty, col.EncodedColumnName, col.Namespace, this ); 
                                        newNode.AppendChild( CreateTextNode( col.ConvertObjectToXml( value ) ) ); 
                                        if ( priorNode != null ) {
                                            node.InsertAfter(newNode, priorNode); 
                                        }
                                        else if ( node.FirstChild != null ) {
                                            node.InsertBefore( newNode, node.FirstChild );
                                        } 
                                        else {
                                            node.AppendChild( newNode ); 
                                        } 
                                        priorNode = newNode;
                                    } 
                                    else {
                                        Debug.Assert( col.ColumnMapping == MappingType.SimpleContent );
                                        newNode = CreateTextNode( col.ConvertObjectToXml( value ) );
                                        if ( node.FirstChild != null ) 
                                            node.InsertBefore( newNode, node.FirstChild );
                                        else 
                                            node.AppendChild( newNode ); 
                                        if ( priorNode == null )
                                            priorNode = newNode; 
                                    }
                                }
                            }
                            else { 
                                if ( col.ColumnMapping == MappingType.SimpleContent ) {
                                    XmlAttribute attr = CreateAttribute( XSI, Keywords.XSI_NIL, Keywords.XSINS ); 
                                    attr.Value = Keywords.TRUE; 
                                    node.SetAttributeNode( attr );
                                    this.bHasXSINIL = true; 
                                }
                            }
                        }
                    } 
                }
                finally { 
                    IgnoreXmlEvents = saveIgnore; 
                    node.ElementState = newState;
                } 
                // update all live pointers
                OnFoliated( node );
            }
        } 

        //Determine best radical insert position for inserting column elements 
        private XmlNode GetColumnInsertAfterLocation( DataRow row, DataColumn col, XmlBoundElement rowElement ) { 
            XmlNode prev = null;
            XmlNode node = null; 

            // text only columns appear first
            if ( IsTextOnly( col ) )
                return null; 

            // insert location must be after free text 
            for ( node = rowElement.FirstChild; node != null; prev = node, node = node.NextSibling ) { 
                if ( !IsTextLikeNode( node ) )
                    break; 
            }

            for ( ; node != null; prev = node, node = node.NextSibling ) {
                // insert location must be before any non-element nodes 
                if ( node.NodeType != XmlNodeType.Element )
                    break; 
                XmlElement e = node as XmlElement; 

                // insert location must be before any non-mapped elements or separate regions 
                if ( mapper.GetRowFromElement( e ) != null )
                    break;

                object schema = mapper.GetColumnSchemaForNode( rowElement, node ); 
                if ( schema == null || !(schema is DataColumn) )
                    break; 
 
                // insert location must be before any columns logically after this column
                if ( ((DataColumn)schema).Ordinal > col.Ordinal ) 
                    break;
            }

            return prev; 
        }
 
        private ArrayList GetNestedChildRelations(DataRow row) { 
            ArrayList list = new ArrayList();
 
            foreach( DataRelation r in row.Table.ChildRelations ) {
                if ( r.Nested )
                    list.Add(r);
            } 

            return list; 
        } 

        private DataRow GetNestedParent(DataRow row) { 
            DataRelation relation = GetNestedParentRelation(row);
            if ( relation != null )
                return row.GetParentRow(relation);
            return null; 
        }
 
        private static DataRelation GetNestedParentRelation( DataRow row ) { 
            DataRelation [] relations = row.Table.NestedParentRelations;
            if (relations.Length == 0) 
                return null;
            return relations[0];
        }
 
        private DataColumn GetTextOnlyColumn( DataRow row ) {
#if DEBUG 
            { 
                // Make sure there is at most only one text column, and the text column (if present) is the one reported by row.Table.XmlText
                DataColumnCollection columns = row.Table.Columns; 
                int cCols = columns.Count;
                int cTextCols = 0;
                for ( int iCol = 0; iCol < cCols; iCol++ ) {
                    DataColumn c = columns[iCol]; 
                    if ( IsTextOnly( c ) ) {
                        Debug.Assert( c == row.Table.XmlText ); 
                        ++cTextCols; 
                    }
                } 
                Debug.Assert( cTextCols == 0 || cTextCols == 1 );
                if ( cTextCols == 0 )
                    Debug.Assert( row.Table.XmlText == null );
            } 
#endif
            return row.Table.XmlText; 
        } 

        ///  
        ///    
        ///       Retrieves the
        ///          DataRow associated with the specified XmlElement.
        ///        
        ///    
        public DataRow GetRowFromElement( XmlElement e ) { 
            return mapper.GetRowFromElement( e ); 
        }
 
        private XmlNode GetRowInsertBeforeLocation(DataRow row, XmlElement rowElement, XmlNode parentElement) {
            DataRow refRow = row;
            int i = 0;
            int pos; 

            // Find position 
            // int pos = row.Table.Rows[row]; 
            for (i = 0; i < row.Table.Rows.Count; i++)
                if (row == row.Table.Rows[i]) 
                    break;
            pos = i;

            DataRow parentRow = GetNestedParent(row); 
            for (i = pos + 1; i < row.Table.Rows.Count; i++) {
                refRow = row.Table.Rows[i]; 
                if (GetNestedParent(refRow) == parentRow && GetElementFromRow(refRow).ParentNode == parentElement) 
                    break;
            } 

            if (i < row.Table.Rows.Count)
                return GetElementFromRow(refRow);
            else 
                return(XmlNode) null;
        } 
 

        ///  
        ///    Retrieves
        ///       the XmlElement associated with the specified DataRow.
        /// 
        public XmlElement GetElementFromRow( DataRow r ) { 
            XmlBoundElement be = r.Element;
            // 
            Debug.Assert( be != null ); 
            return be;
        } 

        internal bool HasPointers( XmlNode node ) {
            while ( true ) {
                try { 
                    if ( pointers.Count > 0 ) {
                        object pointer = null; 
                        foreach( DictionaryEntry entry in pointers ) { 
                            pointer = entry.Value;
                            Debug.Assert( pointer != null ); 
                            if ( ((IXmlDataVirtualNode)pointer).IsOnNode( node ) )
                                return true;
                        }
                    } 
                    return false;
                } 
                catch (Exception e) { 
                    // This can happens only when some threads are creating navigators (thus modifying this.pointers) while other threads are in the foreach loop.
                    // Solution is to re-try HasPointers. 
                    //
                    if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) {
                        throw;
                    } 
                }
            } 
            //should never get to this point due to while (true) loop 
        }
 
        internal bool IgnoreXmlEvents {
            get { return ignoreXmlEvents;}
            set { ignoreXmlEvents = value;}
        } 

        internal bool IgnoreDataSetEvents { 
            get { return this.ignoreDataSetEvents; } 
            set { this.ignoreDataSetEvents = value; }
        } 

        private bool IsFoliated( XmlElement element ) {
            if ( element is XmlBoundElement ) {
                return((XmlBoundElement)element).IsFoliated; 
            }
 
            return true; 
        }
        private bool IsFoliated( XmlBoundElement be ) { 
            return be.IsFoliated;
        }

        internal bool IsFoliationEnabled { 
            get { return isFoliationEnabled; }
            set { isFoliationEnabled = value; } 
        } 

        // This creates a tree and synchronize ROM w/ the created tree. 
        // 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.
        // It takes advantage of the fAssociateDataRow flag for populated mode, which allows creation of XmlBoundElement w/o associating DataRow objects.
        internal XmlNode CloneTree( DataPointer other ) {
            EnsurePopulatedMode(); 

            bool oldIgnoreDataSetEvents = ignoreDataSetEvents; 
            bool oldIgnoreXmlEvents     = ignoreXmlEvents; 
            bool oldFoliationEnabled    = IsFoliationEnabled;
            bool oldAssociateDataRow    = fAssociateDataRow; 

            // Caller should ensure that the EnforceConstraints == false. See 60486 for more info about why this was changed from DataSet.EnforceConstraints = false to an assert.
            Debug.Assert( DataSet.EnforceConstraints == false );
            XmlNode newNode; 

            try { 
                ignoreDataSetEvents = true; 
                ignoreXmlEvents     = true;
                IsFoliationEnabled  = false; 
                fAssociateDataRow   = false;

                // Create the diconnected tree based on the other navigator
                newNode = CloneTreeInternal( other ); 
                Debug.Assert( newNode != null );
 
                // Synchronize DataSet from XML 
                LoadRows( null, newNode );
                SyncRows( null, newNode, false ); 
            }
            finally {
                ignoreDataSetEvents = oldIgnoreDataSetEvents;
                ignoreXmlEvents     = oldIgnoreXmlEvents; 
                IsFoliationEnabled  = oldFoliationEnabled;
                fAssociateDataRow   = oldAssociateDataRow; 
            } 
            return newNode;
        } 

        private XmlNode CloneTreeInternal( DataPointer other ) {
            Debug.Assert( ignoreDataSetEvents == true );
            Debug.Assert( ignoreXmlEvents == true ); 
            Debug.Assert( IsFoliationEnabled == false );
 
            // Create the diconnected tree based on the other navigator 
            XmlNode newNode = CloneNode( other );
 
            DataPointer dp = new DataPointer( other );
            try {
                dp.AddPointer();
                if (newNode.NodeType == XmlNodeType.Element) { 
                    int cAttributes = dp.AttributeCount;
                    for ( int i = 0; i < cAttributes; i++ ) { 
                        dp.MoveToOwnerElement(); 
                        if( dp.MoveToAttribute(i) ) {
                            newNode.Attributes.Append( (XmlAttribute)CloneTreeInternal(dp) ); 
                        }
                    }

                    dp.MoveTo( other ); 
                }
 
                // 
                for ( bool fMore = dp.MoveToFirstChild(); fMore; fMore = dp.MoveToNextSibling() )
                    newNode.AppendChild( CloneTreeInternal( dp ) ); 
            }
            finally {
                dp.SetNoLongerUse();
            } 

            return newNode; 
        } 

        ///  
        ///    [To be supplied.]
        /// 
        public override XmlNode CloneNode( bool deep ) {
            XmlDataDocument clone = (XmlDataDocument)(base.CloneNode(false)); 
            clone.Init(this.DataSet.Clone());
 
            clone.dataSet.EnforceConstraints = this.dataSet.EnforceConstraints; 
            Debug.Assert( clone.FirstChild == null );
            if ( deep ) { 
                DataPointer dp = new DataPointer( this, this );
                try {
                    dp.AddPointer();
                    for ( bool fMore = dp.MoveToFirstChild(); fMore; fMore = dp.MoveToNextSibling() ) { 
                        XmlNode cloneNode;
                        if ( dp.NodeType == XmlNodeType.Element ) 
                            cloneNode = clone.CloneTree( dp ); 
                        else
                            cloneNode = clone.CloneNode( dp ); 
                        clone.AppendChild( cloneNode );
                    }
                }
                finally{ 
                    dp.SetNoLongerUse();
                } 
            } 

            return clone; 
        }

        private XmlNode CloneNode( DataPointer dp ) {
            switch (dp.NodeType) { 
                //for the nodes without value and have no children
                case XmlNodeType.DocumentFragment: 
                    return this.CreateDocumentFragment(); 
                case XmlNodeType.DocumentType:
                    // 
                    return this.CreateDocumentType( dp.Name, dp.PublicId, dp.SystemId, dp.InternalSubset );
                case XmlNodeType.XmlDeclaration:
                    return this.CreateXmlDeclaration( dp.Version, dp.Encoding, dp.Standalone );
                //case XmlNodeType.Notation: -- notation should not be able to be accessed by XmlNavigator 
                    //return this.CreateNotation(dp.Name, dp.PublicId, dp.SystemId );
 
                //for the nodes with value but no children 
                case XmlNodeType.Text:
                    return this.CreateTextNode( dp.Value ); 
                case XmlNodeType.CDATA:
                    return this.CreateCDataSection( dp.Value );
                case XmlNodeType.ProcessingInstruction:
                    return this.CreateProcessingInstruction( dp.Name, dp.Value ); 
                case XmlNodeType.Comment:
                    return this.CreateComment( dp.Value ); 
                case XmlNodeType.Whitespace: 
                    return this.CreateWhitespace( dp.Value );
                case XmlNodeType.SignificantWhitespace: 
                    return this.CreateSignificantWhitespace( dp.Value );
                //for the nodes that don't have values, but might have children -- only clone the node and leave the children untouched
                case XmlNodeType.Element:
                    return this.CreateElement(dp.Prefix, dp.LocalName, dp.NamespaceURI); 
                case XmlNodeType.Attribute:
                    return this.CreateAttribute(dp.Prefix, dp.LocalName, dp.NamespaceURI); 
                case XmlNodeType.EntityReference: 
                    return this.CreateEntityReference( dp.Name );
            } 
            throw new InvalidOperationException( Res.GetString(Res.DataDom_CloneNode, dp.NodeType.ToString() ) );
        }

        internal static bool IsTextLikeNode( XmlNode n ) { 
            switch ( n.NodeType ) {
                case XmlNodeType.Text: 
                case XmlNodeType.CDATA: 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace: 
                    return true;

                case XmlNodeType.EntityReference:
                    Debug.Assert( false ); 
                    return false;
 
                default: 
                    return false;
            } 
        }

        internal bool IsNotMapped( DataColumn c ) {
            return DataSetMapper.IsNotMapped( c ); 
        }
 
        private bool IsSame( DataColumn c, int recNo1, int recNo2 ) { 
            if ( c.Compare( recNo1, recNo2 ) == 0 )
                return true; 

            return false;
        }
 
        internal bool IsTextOnly( DataColumn c ) {
            return c.ColumnMapping == MappingType.SimpleContent; 
        } 

 
        /// 
        ///    Loads the XML document from the specified file.
        /// 
        public override void Load( string filename ) { 
            this.bForceExpandEntity = true;
            base.Load( filename ); 
            this.bForceExpandEntity = false; 
        }
 
        /// 
        ///    Loads the XML document from the specified Stream.
        /// 
        public override void Load( Stream inStream ) { 
            this.bForceExpandEntity = true;
            base.Load( inStream ); 
            this.bForceExpandEntity = false; 
        }
 
        /// 
        ///    Loads the XML document from the specified TextReader.
        /// 
        public override void Load( TextReader txtReader ) { 
            this.bForceExpandEntity = true;
            base.Load( txtReader ); 
            this.bForceExpandEntity = false; 
        }
 
        /// 
        ///    Loads the XML document from the specified XmlReader.
        /// 
        public override void Load( XmlReader reader ) { 
            if ( this.FirstChild != null )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_MultipleLoad ) ); 
 
            try {
                ignoreXmlEvents = true; 

                // Unhook the DataRowCreatedSpecial listener, since we no longer base on the first created DataRow to do the Bind
                if ( fDataRowCreatedSpecial )
                    UnBindSpecialListeners(); 

                // We should NOT create DataRow objects when calling XmlDataDocument.CreateElement 
                fAssociateDataRow = false; 
                // Foliation s/b disabled
                isFoliationEnabled = false; 

                //now if we load from file we need to set the ExpandEntity flag to ExpandEntities
                if ( this.bForceExpandEntity ) {
                    Debug.Assert( reader is XmlTextReader ); 
                    ((XmlTextReader)reader).EntityHandling = EntityHandling.ExpandEntities;
                } 
                base.Load( reader ); 
                BindForLoad();
            } 
            finally {
                ignoreXmlEvents = false;
                isFoliationEnabled = true;
                autoFoliationState = ElementState.StrongFoliation; 
                fAssociateDataRow = true;
            } 
        } 

        private void LoadDataSetFromTree() { 
            ignoreDataSetEvents = true;
            ignoreXmlEvents = true;
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false; 
            bool saveEnforce = dataSet.EnforceConstraints;
            dataSet.EnforceConstraints = false; 
 
            try {
                Debug.Assert( DocumentElement != null ); 
                LoadRows( null, DocumentElement );
                SyncRows( null, DocumentElement, true );

                dataSet.EnforceConstraints = saveEnforce; 
            }
            finally { 
                ignoreDataSetEvents = false; 
                ignoreXmlEvents = false;
                IsFoliationEnabled = wasFoliationEnabled; 
            }
        }

        private void LoadTreeFromDataSet( DataSet ds ) { 
            ignoreDataSetEvents = true;
            ignoreXmlEvents = true; 
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled = false;
            this.fAssociateDataRow = false; 

            DataTable [] orderedTables = OrderTables(ds); // this  is to fix WebData 103397
            // problem is after we add support for Namespace  for DataTable, when infering we do not guarantee that table would be
            // in the same sequence that they were in XML because of namespace, some would be on different schema, so since they 
            // wont be in the same sequence as in XML, we may end up with having a child table, before its parent (which is not doable
            // with XML; and this happend because they are in different namespace) 
            // this kind of problems are known and please see comment in "OnNestedParentChange" 
            // 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
 
            try {
                for(int i = 0; i < orderedTables.Length; i++) {
                    DataTable t = orderedTables[i];
                    foreach( DataRow r in t.Rows ) { 
                        Debug.Assert( r.Element == null );
                        XmlBoundElement rowElem = AttachBoundElementToDataRow( r ); 
                        // 
                        switch ( r.RowState ) {
                        case DataRowState.Added: 
                        case DataRowState.Unchanged:
                        case DataRowState.Modified:
                            //
                            OnAddRow( r ); 
                            break;
                        case DataRowState.Deleted: 
                            // Nothing to do (the row already has an associated element as a fragment 
                            break;
                        case DataRowState.Detached: 
                            // We should not get rows in this state
                            Debug.Assert( false );
                            break;
                        default: 
                            // Unknown row state
                            Debug.Assert( false ); 
                            break; 
                        }
                    } 
                }
            }
            finally {
                ignoreDataSetEvents = false; 
                ignoreXmlEvents = false;
                IsFoliationEnabled = wasFoliationEnabled; 
                this.fAssociateDataRow = true; 
            }
        } 

        // load all data from tree structre into datarows
        private void LoadRows( XmlBoundElement rowElem, XmlNode node ) {
            Debug.Assert( node != null ); 

            XmlBoundElement be = node as XmlBoundElement; 
            if ( be != null ) { 
                DataTable dt = mapper.SearchMatchingTableSchema( rowElem, be );
 
                if ( dt != null ) {
                    DataRow r = GetRowFromElement( be );
                    Debug.Assert( r == null );
                    // If the rowElement was just created and has an un-initialized 
                    if ( be.ElementState == ElementState.None )
                        be.ElementState = ElementState.WeakFoliation; 
                    r = dt.CreateEmptyRow(); 
                    Bind( r, be );
 
                    // the region rowElem is now be
                    Debug.Assert( be.Row != null );
                    rowElem = be;
                } 
            }
            // recurse down for children 
            for ( XmlNode child = node.FirstChild; child != null; child = child.NextSibling ) 
                LoadRows( rowElem, child );
        } 

        internal DataSetMapper Mapper {
            get {
                return mapper; 
            }
        } 
 
        internal void OnDataRowCreated( object oDataSet, DataRow row ) {
            Debug.Assert( row.RowState == DataRowState.Detached ); 
            OnNewRow( row );
        }

        internal void OnClearCalled( object oDataSet, DataTable table ) { 
            throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_Clear ) );
        } 
 
        internal void OnDataRowCreatedSpecial( object oDataSet, DataRow row ) {
            Debug.Assert( row.RowState == DataRowState.Detached ); 

            // Register the regular events and un-register this one
            Bind(true);
            // Pass the event to the regular listener 
            OnNewRow( row );
        } 
        // Called when a new DataRow is created 
        internal void OnNewRow( DataRow row ) {
            Debug.Assert( row.Element == null ); 
            // Allow New state also because we are calling this function from
            Debug.Assert( row.RowState == DataRowState.Detached );

            AttachBoundElementToDataRow( row ); 
        }
 
        private XmlBoundElement AttachBoundElementToDataRow( DataRow row ) { 
            Debug.Assert( row.Element == null );
            DataTable table = row.Table; 
            // We shoould NOT call CreateElement here, since CreateElement will create and attach a new DataRow to the element
            XmlBoundElement rowElement = new XmlBoundElement( string.Empty, table.EncodedTableName, table.Namespace, this );
            rowElement.IsEmpty = false;
            Bind( row, rowElement ); 
            rowElement.ElementState = ElementState.Defoliated;
            return rowElement; 
        } 

        private bool NeedXSI_NilAttr( DataRow row ) { 
            DataTable tb = row.Table;
            Debug.Assert( tb != null ) ;
            if ( tb.xmlText == null )
                return false; 
            object value = row[tb.xmlText];
            return ( Convert.IsDBNull( value ) ); 
        } 

        private void OnAddRow( DataRow row ) { 
            // Xml operations in this func should not trigger ROM operations
            Debug.Assert( this.ignoreXmlEvents == true );

            XmlBoundElement rowElement = (XmlBoundElement)(GetElementFromRow( row )); 
            Debug.Assert( rowElement != null );
 
            if ( NeedXSI_NilAttr( row ) && !rowElement.IsFoliated ) 
                //we need to foliate it because we need to add one more attribute xsi:nil = true;
                ForceFoliation( rowElement, AutoFoliationState ); 

            Debug.Assert( rowElement != null );
            DataRow rowDocElem = GetRowFromElement( this.DocumentElement );
            if ( rowDocElem != null ) { 
                DataRow parentRow = GetNestedParent( row );
                if ( parentRow == null ) 
                    DemoteDocumentElement(); 
            }
            EnsureDocumentElement().AppendChild( rowElement ); 

            // Move the children of the row under
            FixNestedChildren( row, rowElement );
            OnNestedParentChange( row, rowElement, null ); 
        }
 
        private void OnColumnValueChanged( DataRow row, DataColumn col, XmlBoundElement rowElement ) { 
            //
            if ( IsNotMapped(col) ) 
                goto lblDoNestedRelationSync;

            object value = row[col];
 
            if ( col.ColumnMapping == MappingType.SimpleContent && Convert.IsDBNull( value ) && !rowElement.IsFoliated )
                ForceFoliation( rowElement, ElementState.WeakFoliation); 
            else { 
                // no need to [....] if not foliated
                if ( !IsFoliated( rowElement ) ) { 
#if DEBUG
                    // If the new value is null, we should be already foliated if there is a DataPointer that points to the column
                    // (see OnRowChanging, case DataRowAction.Change)
                    if ( Convert.IsDBNull( row[col, DataRowVersion.Current] ) ) { 
                        try {
                            if ( pointers.Count > 0 ) { 
                                object pointer = null; 
                                foreach( DictionaryEntry entry in pointers ) {
                                    pointer = entry.Value; 
                                    Debug.Assert( (pointer != null) && ! ((IXmlDataVirtualNode)pointer).IsOnColumn( col ) );
                                }
                            }
                        } 
                        catch (Exception e) {
                            // 
                            if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) { 
                                throw;
                            } 
                            // 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
                        }
                    }
#endif 
                    goto lblDoNestedRelationSync;
                } 
            } 

            if ( IsTextOnly( col ) ) { 
                if ( Convert.IsDBNull( value ) ) {
                    value = String.Empty;
                    //make sure that rowElement has Attribute xsi:nil and its value is true
                    XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL); 
                    if ( attr == null ) {
                        attr = CreateAttribute( XSI, Keywords.XSI_NIL, Keywords.XSINS ); 
                        attr.Value = Keywords.TRUE; 
                        rowElement.SetAttributeNode( attr );
                        this.bHasXSINIL = true; 
                    }
                    else
                        attr.Value = Keywords.TRUE;
                } 
                else {
                    //make sure that if rowElement has Attribute xsi:nil, its value is false 
                    XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL); 
                    if ( attr != null )
                        attr.Value = Keywords.FALSE; 
                }
                ReplaceInitialChildText( rowElement, col.ConvertObjectToXml( value ) );
                goto lblDoNestedRelationSync;
            } 

            // update the attribute that maps to the column 
            bool fFound = false; 

            // Find the field node and set it's value 
            if (col.ColumnMapping == MappingType.Attribute) {
                foreach( XmlAttribute attr in rowElement.Attributes ) {
                    if ( attr.LocalName == col.EncodedColumnName && attr.NamespaceURI == col.Namespace ) {
                        if ( Convert.IsDBNull( value ) ) { 
                            attr.OwnerElement.Attributes.Remove( attr );
                        } 
                        else { 
                            attr.Value = col.ConvertObjectToXml( value );
                        } 
                        fFound = true;
                        break;
                    }
                } 

                // create new attribute if we didn't find one. 
                if ( !fFound && ! Convert.IsDBNull( value ) ) { 
                    rowElement.SetAttribute( col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml( value ) );
                } 
            }
            else {
                // update elements that map to the column...
                RegionIterator iter = new RegionIterator( (XmlBoundElement)rowElement ); 
                bool fMore = iter.Next();
                while ( fMore ) { 
                    if ( iter.CurrentNode.NodeType == XmlNodeType.Element ) { 
                        XmlElement e = (XmlElement) iter.CurrentNode;
                        Debug.Assert( e != null ); 
                        //we should skip the subregion
                        XmlBoundElement be = e as XmlBoundElement;
                        if ( be != null && be.Row != null ) {
                            fMore = iter.NextRight(); //skip over the sub-region 
                            continue;
                        } 
                        if ( e.LocalName == col.EncodedColumnName && e.NamespaceURI == col.Namespace ) { 
                            fFound = true;
                            if ( Convert.IsDBNull( value ) ) { 
                                PromoteNonValueChildren( e );
                                fMore = iter.NextRight();
                                e.ParentNode.RemoveChild( e );
                                // keep looking for more matching elements 
                                continue;
                            } 
                            else { 
                                ReplaceInitialChildText( e, col.ConvertObjectToXml( value ) );
                                //make sure that if the Element has Attribute xsi:nil, its value is false 
                                XmlAttribute attr = e.GetAttributeNode(XSI_NIL);
                                if ( attr != null )
                                    attr.Value = Keywords.FALSE;
                                // no need to look any further. 
                                goto lblDoNestedRelationSync;
                            } 
                        } 
                    }
                    fMore = iter.Next(); 
                }

                // create new element if we didn't find one.
                if ( !fFound && ! Convert.IsDBNull( value ) ) { 
                    XmlElement newElem = new XmlBoundElement( string.Empty, col.EncodedColumnName, col.Namespace, this );
                    newElem.AppendChild( CreateTextNode( col.ConvertObjectToXml( value ) ) ); 
 
                    XmlNode elemBefore = GetColumnInsertAfterLocation( row, col, rowElement );
                    if ( elemBefore != null ) { 
                        rowElement.InsertAfter( newElem, elemBefore );
                    }
                    else if ( rowElement.FirstChild != null ) {
                        rowElement.InsertBefore( newElem, rowElement.FirstChild ); 
                    }
                    else { 
                        rowElement.AppendChild( newElem ); 
                    }
                } 
            }
lblDoNestedRelationSync:
            // Change the XML to conform to the (potentially) change in parent nested relation
            DataRelation relation = GetNestedParentRelation(row); 
            if ( relation != null ) {
                Debug.Assert( relation.ChildTable == row.Table ); 
                if ( relation.ChildKey.ContainsColumn( col ) ) 
                    OnNestedParentChange( row, rowElement, col );
            } 
        }

        private void OnColumnChanged( object sender, DataColumnChangeEventArgs args ) {
            // You should not be able to make DataRow field changes if the DataRow is deleted 
            Debug.Assert( args.Row.RowState != DataRowState.Deleted );
 
            if ( ignoreDataSetEvents ) 
                return;
 
            bool wasIgnoreXmlEvents = ignoreXmlEvents;
            ignoreXmlEvents = true;
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false; 

            try { 
                DataRow row     = args.Row; 
                DataColumn col  = args.Column;
                object oVal     = args.ProposedValue; 

                if ( row.RowState == DataRowState.Detached ) {
                    XmlBoundElement be = row.Element;
                    Debug.Assert( be != null ); 
                    if ( be.IsFoliated ) {
                        // Need to [....] changes from ROM to DOM 
                        OnColumnValueChanged( row, col, be ); 
                    }
                } 
            }
            finally {
                IsFoliationEnabled = wasFoliationEnabled;
                ignoreXmlEvents = wasIgnoreXmlEvents; 
            }
        } 
 
        private void OnColumnValuesChanged( DataRow row, XmlBoundElement rowElement ) {
            Debug.Assert( row != null ); 
            Debug.Assert( rowElement != null );

            // If user has cascading relationships, then columnChangeList will contains the changed columns only for the last row beeing cascaded
            // but there will be multiple ROM events 
            if ( columnChangeList.Count > 0 ) {
                if ( ((DataColumn)(columnChangeList[0])).Table == row.Table ) { 
                    foreach( DataColumn c in columnChangeList ) 
                        OnColumnValueChanged( row, c, rowElement );
                } 
                else {
                    foreach( DataColumn c in row.Table.Columns )
                        OnColumnValueChanged( row, c, rowElement );
                } 
            }
            else { 
                foreach( DataColumn c in row.Table.Columns ) 
                    OnColumnValueChanged( row, c, rowElement );
            } 
            columnChangeList.Clear();
        }

        private void OnDeleteRow( DataRow row, XmlBoundElement rowElement ) { 
            // IgnoreXmlEvents s/b on since we are manipulating the XML tree and we not want this to reflect in ROM view.
            Debug.Assert( this.ignoreXmlEvents == true ); 
            // Special case when rowElem is document element: we create a new docElem, move the current one as a child of 
            // the new created docElem, then process as if the docElem is not a rowElem
            if ( rowElement == this.DocumentElement ) 
                DemoteDocumentElement();

            PromoteInnerRegions( rowElement );
            rowElement.ParentNode.RemoveChild( rowElement ); 
        }
 
        private void OnDeletingRow( DataRow row, XmlBoundElement rowElement ) { 
            // Note that this function is beeing called even if ignoreDataSetEvents == true.
 
            // Foliate, so we can be able to preserve the nodes even if the DataRow has no longer values for the crtRecord.
            if ( IsFoliated( rowElement ) )
                return;
 
            bool wasIgnoreXmlEvents  = IgnoreXmlEvents;
            IgnoreXmlEvents          = true; 
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled       = true;
            try { 
                Foliate( rowElement );
            }
            finally {
                IsFoliationEnabled = wasFoliationEnabled; 
                IgnoreXmlEvents    = wasIgnoreXmlEvents;
            } 
        } 

        private void OnFoliated( XmlNode node ) { 
            while ( true ) {
                try {
                    if ( pointers.Count > 0 ) {
                        foreach( DictionaryEntry entry in pointers ) { 
                            object pointer = entry.Value;
                            Debug.Assert( pointer != null ); 
                            ((IXmlDataVirtualNode)pointer).OnFoliated( node ); 
                        }
                    } 
                    return;
                }
                catch (Exception e) {
                    // 
                    if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) {
                        throw; 
                    } 
                    // This can happens only when some threads are creating navigators (thus modifying this.pointers) while other threads are in the foreach loop.
                    // Solution is to re-try OnFoliated. 
                }
            }
            // You should never get here in regular cases
        } 

        DataColumn FindAssociatedParentColumn( DataRelation relation, DataColumn childCol ) { 
            DataColumn[] columns = relation.ChildKey.ColumnsReference; 
            for (int i = 0; i < columns.Length; i++) {
                if ( childCol == columns[i] ) 
                    return relation.ParentKey.ColumnsReference[i];
            }
            return null;
        } 

        // Change the childElement position in the tree to conform to the parent nested relationship in ROM 
        private void OnNestedParentChange(DataRow child, XmlBoundElement childElement, DataColumn childCol) { 
            Debug.Assert( child.Element == childElement && childElement.Row == child );
            // This function is (and s/b) called as a result of ROM changes, therefore XML changes done here should not be [....]-ed to ROM 
            Debug.Assert( ignoreXmlEvents == true );
#if DEBUG
            // In order to check that this move does not change the connected/disconnected state of the node
            bool fChildElementConnected = IsConnected( childElement ); 
#endif
            DataRow parentRowInTree; 
            if ( childElement == this.DocumentElement || childElement.ParentNode == null ) 
                parentRowInTree = null;
            else 
                parentRowInTree = GetRowFromElement( (XmlElement) childElement.ParentNode );
            DataRow parentRowInRelation = GetNestedParent(child);

            if ( parentRowInTree != parentRowInRelation ) { 
                if ( parentRowInRelation != null ) {
                    // 
 
                    XmlElement newParent = GetElementFromRow( parentRowInRelation );
                    newParent.AppendChild( childElement ); 
                }
                else {
                    // no parent? Maybe the parentRow is during changing or childCol is the ID is set to null ( detached from the parent row ).
                    DataRelation relation = GetNestedParentRelation(child); 
                    if ( childCol == null || relation == null || Convert.IsDBNull(child[childCol]) ) {
                        EnsureNonRowDocumentElement().AppendChild( childElement ); 
                    } 
                    else {
                        DataColumn colInParent = FindAssociatedParentColumn( relation, childCol ); 
                        Debug.Assert( colInParent != null );
                        object comparedValue = colInParent.ConvertValue(child[childCol]);
                        if (parentRowInTree.tempRecord != -1 && colInParent.CompareValueTo( parentRowInTree.tempRecord, comparedValue ) != 0 ) {
                            EnsureNonRowDocumentElement().AppendChild( childElement ); 
                        }
                        //else do nothing because its original parentRowInRelation will be changed so that this row will still be its child 
                    } 
                }
            } 
#if DEBUG
            // 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
            // 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)
            // 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. 
            Debug.Assert( fChildElementConnected == IsConnected( childElement ) );
            Debug.Assert( IsRowLive( child ) ? IsConnected( childElement ) : ! IsConnected( childElement ) ); 
#endif 
        }
 
        private void OnNodeChanged( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents )
                return;
 
            bool wasIgnoreDataSetEvents = ignoreDataSetEvents;
            bool wasIgnoreXmlEvents     = ignoreXmlEvents; 
            bool wasFoliationEnabled    = IsFoliationEnabled; 
            ignoreDataSetEvents = true;
            ignoreXmlEvents     = true; 
            IsFoliationEnabled  = false;
            bool fEnableCascading = DataSet.fEnableCascading;
            DataSet.fEnableCascading = false;
 
            try {
                // okay to allow text node value changes when bound. 
                XmlBoundElement rowElement = null; 

                Debug.Assert( DataSet.EnforceConstraints == false ); 

                if ( mapper.GetRegion( args.Node, out rowElement ) ) {
                    SynchronizeRowFromRowElement( rowElement );
                } 
            }
            finally { 
                ignoreDataSetEvents = wasIgnoreDataSetEvents; 
                ignoreXmlEvents     = wasIgnoreXmlEvents;
                IsFoliationEnabled  = wasFoliationEnabled; 
                DataSet.fEnableCascading = fEnableCascading;
            }
        }
 
        private void OnNodeChanging( object sender, XmlNodeChangedEventArgs args ) {
            if( ignoreXmlEvents ) 
                return; 
            if ( DataSet.EnforceConstraints != false )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_EnforceConstraintsShouldBeOff ) ); 
        }


        private void OnNodeInserted( object sender, XmlNodeChangedEventArgs args ) { 
            if ( ignoreXmlEvents )
                return; 
 
            bool wasIgnoreDataSetEvents = ignoreDataSetEvents;
            bool wasIgnoreXmlEvents     = ignoreXmlEvents; 
            bool wasFoliationEnabled    = IsFoliationEnabled;
            ignoreDataSetEvents = true;
            ignoreXmlEvents     = true;
            IsFoliationEnabled  = false; 

            Debug.Assert( DataSet.EnforceConstraints == false ); 
 
            bool fEnableCascading = DataSet.fEnableCascading;
            DataSet.fEnableCascading = false; 

            try {
                // Handle both new node inserted and 2nd part of a move operation.
                // 
                XmlNode node = args.Node;
                XmlNode oldParent = args.OldParent; 
                XmlNode newParent = args.NewParent; 

                // 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. 
                Debug.Assert( oldParent == null );
                if ( IsConnected( newParent ) ) {
                    // Inserting a node to connected tree
                    OnNodeInsertedInTree( node ); 
                }
                else { 
                    // Inserting a node to disconnected tree 
                    OnNodeInsertedInFragment( node );
                } 
            }
            finally {
                ignoreDataSetEvents = wasIgnoreDataSetEvents;
                ignoreXmlEvents     = wasIgnoreXmlEvents; 
                IsFoliationEnabled  = wasFoliationEnabled;
                DataSet.fEnableCascading = fEnableCascading; 
            } 

        } 

        private void OnNodeInserting( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents )
                return; 
            if ( DataSet.EnforceConstraints != false )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_EnforceConstraintsShouldBeOff ) ); 
        } 

 
        private void OnNodeRemoved( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents )
                return;
 
            bool wasIgnoreDataSetEvents = ignoreDataSetEvents;
            bool wasIgnoreXmlEvents     = ignoreXmlEvents; 
            bool wasFoliationEnabled    = IsFoliationEnabled; 
            ignoreDataSetEvents = true;
            ignoreXmlEvents     = true; 
            IsFoliationEnabled  = false;

            Debug.Assert( DataSet.EnforceConstraints == false );
 
            bool fEnableCascading = DataSet.fEnableCascading;
            DataSet.fEnableCascading = false; 
 
            try {
                XmlNode node = args.Node; 
                XmlNode oldParent = args.OldParent;
                Debug.Assert( args.NewParent == null );

                if ( IsConnected( oldParent ) ) { 
                    // Removing from connected tree to disconnected tree
                    OnNodeRemovedFromTree( node, oldParent ); 
                } 
                else {
                    // Removing from disconnected tree to disconnected tree: just [....] the old region 
                    OnNodeRemovedFromFragment( node, oldParent );
                }
            }
            finally { 
                ignoreDataSetEvents = wasIgnoreDataSetEvents;
                ignoreXmlEvents     = wasIgnoreXmlEvents; 
                IsFoliationEnabled  = wasFoliationEnabled; 
                DataSet.fEnableCascading = fEnableCascading;
            } 
        }

        private void OnNodeRemoving( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents ) 
                return;
            if ( DataSet.EnforceConstraints != false ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_EnforceConstraintsShouldBeOff ) ); 
        }
 
        // Node was removed from connected tree to disconnected tree
        private void OnNodeRemovedFromTree( XmlNode node, XmlNode oldParent ) {
            XmlBoundElement oldRowElem;
 
            // Synchronize values from old region
            if ( mapper.GetRegion( oldParent, out oldRowElem ) ) 
                SynchronizeRowFromRowElement( oldRowElem ); 

            // Disconnect all regions, starting w/ node (if it is a row-elem) 
            XmlBoundElement rowElem = node as XmlBoundElement;
            if ( rowElem != null && rowElem.Row != null )
                EnsureDisconnectedDataRow( rowElem );
            TreeIterator iter = new TreeIterator( node ); 
            for ( bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement() ) {
                rowElem = (XmlBoundElement)(iter.CurrentNode); 
                EnsureDisconnectedDataRow( rowElem ); 
            }
 
            // Assert that all sub-regions are disconnected
            AssertNonLiveRows( node );
        }
        // Node was removed from the disconnected tree to disconnected tree 
        private void OnNodeRemovedFromFragment( XmlNode node, XmlNode oldParent ) {
            XmlBoundElement oldRowElem; 
 
            if ( mapper.GetRegion( oldParent, out oldRowElem ) ) {
                // [....] the old region if it is not deleted 
                DataRow row = oldRowElem.Row;
                // Since the old old region was disconnected, then the row can be only Deleted or Detached
                Debug.Assert( ! IsRowLive( row ) );
                if ( oldRowElem.Row.RowState == DataRowState.Detached ) 
                    SynchronizeRowFromRowElement( oldRowElem );
            } 
 
            // Need to set nested for the sub-regions (if node is a row-elem, we need to set it just for itself)
            XmlBoundElement be = node as XmlBoundElement; 
            if ( be != null && be.Row != null ) {
                Debug.Assert( ! IsRowLive( be.Row ) );
                SetNestedParentRegion( be, null );
            } 
            else {
                // Set nested parent to null for all child regions 
                TreeIterator iter = new TreeIterator( node ); 
                for ( bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() ) {
                    XmlBoundElement rowElemChild = (XmlBoundElement)(iter.CurrentNode); 
                    SetNestedParentRegion( rowElemChild, null );
                }
            }
 
            // Assert that all sub-regions are disconnected
            AssertNonLiveRows( node ); 
        } 

 
        private void OnRowChanged( object sender, DataRowChangeEventArgs args ) {
            if ( ignoreDataSetEvents )
                return;
 
            ignoreXmlEvents = true;
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled = false; 

            try { 
                DataRow row = args.Row;
                XmlBoundElement rowElement = row.Element;
                // We should have an associated row-elem created when the DataRow was created (or at the load time)
                Debug.Assert( rowElement != null ); 

                switch ( args.Action ) { 
                    case DataRowAction.Add: 
                        //
 



 

 
 
                        OnAddRow( row );
                        break; 

                    case DataRowAction.Delete:
                        OnDeleteRow( row, rowElement );
                        break; 

                    case DataRowAction.Rollback: 
                        switch ( rollbackState ) { 
                            case DataRowState.Deleted:
                                OnUndeleteRow( row, rowElement ); 
                                UpdateAllColumns( row, rowElement );
                                break;

                            case DataRowState.Added: 
                                rowElement.ParentNode.RemoveChild( rowElement );
                                break; 
 
                            case DataRowState.Modified:
                                OnColumnValuesChanged( row, rowElement ); 
                                break;
                        }
                        break;
 
                    case DataRowAction.Change:
                        OnColumnValuesChanged( row, rowElement ); 
                        break; 

                    case DataRowAction.Commit: 
                        if ( row.RowState == DataRowState.Detached ) {
                            //by now, all the descendent of the element that is not of this region should have been promoted already
                            rowElement.RemoveAll();
                        } 
                        break;
                    default: 
                        //Console.WriteLine("Other Event"); 
                        break;
                } 
            }
            finally {
                IsFoliationEnabled = wasFoliationEnabled;
                ignoreXmlEvents = false; 
            }
        } 
 
        private void OnRowChanging( object sender, DataRowChangeEventArgs args ) {
            // We foliate the region each time the assocaited row gets deleted 
            DataRow row = args.Row;
            if ( args.Action == DataRowAction.Delete && row.Element != null ) {
                OnDeletingRow( row, row.Element );
                return; 
            }
 
            if ( ignoreDataSetEvents ) 
                return;
 
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false;

            try { 
                ignoreXmlEvents = true;
 
                XmlElement rowElement = GetElementFromRow( row ); 

                int nRec1 = -1; 
                int nRec2 = -1;

                if ( rowElement != null ) {
                    switch ( args.Action ) { 
                        case DataRowAction.Add:
                            // DataRow is beeing added to the table (Table.Rows.Add is beeing called) 
                            break; 

                        case DataRowAction.Delete: 
                            // DataRow is beeing deleted
                            //    - state transition from New (AKA PendingInsert) to Detached (AKA Created)
                            //    - state transition from Unchanged to Deleted (AKA PendingDelete)
                            //    - state transition from Modified (AKA PendingChange) to Delete (AKA PendingDelete) 
                            Debug.Assert( false );  // This should have been handled above, irrespective of ignoreDataSetEvents value (true or false)
                            break; 
 
                        case DataRowAction.Rollback:
                            // DataRow gets reverted to previous values (by calling DataRow.RejectChanges): 
                            //    - state transition from Detached (AKA Created) to Detached (AKA Created)
                            //    - state transition from New (AKA PendingInsert) to Detached (AKA Created)
                            //    - state transition from Modified (AKA PendingChange) to Unchanged
                            //    - state transition from Deleted (AKA PendingDelete) to Unchanged 
                            rollbackState = row.RowState;
                            switch ( rollbackState ) { 
                                case DataRowState.Deleted: 
                                    break;
 
                                case DataRowState.Detached:
                                    break;

                                case DataRowState.Added: 
                                    break;
 
                                case DataRowState.Modified: 
                                    columnChangeList.Clear();
                                    nRec1 = row.GetRecordFromVersion(DataRowVersion.Original); 
                                    nRec2 = row.GetRecordFromVersion(DataRowVersion.Current);
                                    foreach( DataColumn c in row.Table.Columns ) {
                                        if ( !IsSame( c, nRec1, nRec2 ) )
                                            columnChangeList.Add(c); 
                                    }
                                    break; 
 
                            }
                            break; 

                        case DataRowAction.Change:
                            // A DataRow field is beeing changed
                            //    - state transition from New (AKA PendingInsert) to New (AKA PendingInsert) 
                            //    - state transition from Unchanged to Modified (AKA PendingChange)
                            //    - state transition from Modified (AKA PendingChange) to Modified (AKA PendingChange) 
                            // 
                            columnChangeList.Clear();
                            nRec1 = row.GetRecordFromVersion( DataRowVersion.Proposed ); 
                            nRec2 = row.GetRecordFromVersion( DataRowVersion.Current );
                            foreach( DataColumn c in row.Table.Columns ) {
                                object proposedValue = row[c, DataRowVersion.Proposed];
                                object currentValue  = row[c, DataRowVersion.Current]; 
                                // Foliate if proposedValue is DBNull; this way the DataPointer objects will point to a disconnected fragment after
                                // the DBNull value is beeing set 
                                if ( Convert.IsDBNull( proposedValue ) && ! Convert.IsDBNull( currentValue ) ) { 
                                    // Foliate only for non-hidden columns (since hidden cols are not represented in XML)
                                    if ( c.ColumnMapping != MappingType.Hidden ) 
                                        FoliateIfDataPointers( row, rowElement );
                                }
                                if ( !IsSame( c, nRec1, nRec2 ) )
                                    columnChangeList.Add(c); 
                            }
                            break; 
 
                        case DataRowAction.Commit:
                            break; 
                    }
                }
            }
            finally { 
                ignoreXmlEvents = false;
                IsFoliationEnabled = wasFoliationEnabled; 
            } 
        }
 
        private void OnDataSetPropertyChanging( object oDataSet, PropertyChangedEventArgs args ) {
            if ( args.PropertyName == "DataSetName" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_DataSetNameChange ) );
            // 
        }
        private void OnColumnPropertyChanging( object oColumn, PropertyChangedEventArgs args ) { 
            if ( args.PropertyName == "ColumnName" ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_ColumnNameChange ) );
            if ( args.PropertyName == "Namespace" ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_ColumnNamespaceChange ) );
            if ( args.PropertyName == "ColumnMapping" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_ColumnMappingChange ) );
        } 
        private void OnTablePropertyChanging( object oTable, PropertyChangedEventArgs args ) {
            if ( args.PropertyName == "TableName" ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_TableNameChange ) ); 
            if ( args.PropertyName == "Namespace" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_TableNamespaceChange ) ); 
        }
        private void OnTableColumnsChanging( object oColumnsCollection, CollectionChangeEventArgs args ) {
            // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
            // args.Element is one of either the column (for Add and Remove actions or null, if the entire colection of columns is changing) 

            // Disallow changing the columns collection (since we are subscribed only in populated mode, we allow changes in any state but non-populated mode) 
            throw new InvalidOperationException(  Res.GetString(Res.DataDom_TableColumnsChange ) ); 
        }
 
        private void OnDataSetTablesChanging( object oTablesCollection, CollectionChangeEventArgs args ) {
            // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
            // args.Element is a table (dont know if it can be null:
 
            // Disallow changing the tables collection (since we are subscribed only in populated mode, we allow changes in any state but non-populated mode)
            throw new InvalidOperationException(  Res.GetString(Res.DataDom_DataSetTablesChange ) ); 
        } 

        private void OnDataSetRelationsChanging( object oRelationsCollection, CollectionChangeEventArgs args ) { 
            // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
            // args.Element is a DataRelation (dont know if it can be null:

            // Disallow changing the tables collection if there is data loaded and there are nested relationship that are added/refreshed 
            DataRelation rel = (DataRelation)(args.Element);
            if ( rel != null && rel.Nested ) 
                throw new InvalidOperationException(  Res.GetString(Res.DataDom_DataSetNestedRelationsChange ) ); 

            // If Add and Remove, we should already been throwing if .Nested == false 
            Debug.Assert( ! (args.Action == CollectionChangeAction.Add || args.Action == CollectionChangeAction.Remove) || rel.Nested == false );
            if ( args.Action == CollectionChangeAction.Refresh ) {
                foreach ( DataRelation relTemp in (DataRelationCollection)oRelationsCollection ) {
                    if ( relTemp.Nested ) { 
                        throw new InvalidOperationException(  Res.GetString(Res.DataDom_DataSetNestedRelationsChange ) );
                    } 
                } 
            }
        } 

        private void OnRelationPropertyChanging( object oRelationsCollection, PropertyChangedEventArgs args ) {
            if ( args.PropertyName == "Nested" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_DataSetNestedRelationsChange ) ); 
        }
 
        private void OnUndeleteRow( DataRow row, XmlElement rowElement ) { 
            XmlNode refRow;
            XmlElement parent; 

            // make certain we weren't place somewhere else.
            if ( rowElement.ParentNode != null )
                rowElement.ParentNode.RemoveChild( rowElement ); 

            // Find the parent of RowNode to be inserted 
            DataRow parentRowInRelation = GetNestedParent(row); 
            if (parentRowInRelation == null) {
                parent = EnsureNonRowDocumentElement(); 
            }
            else
                parent = GetElementFromRow(parentRowInRelation);
 
            if ((refRow = GetRowInsertBeforeLocation(row, rowElement, parent)) != null)
                parent.InsertBefore(rowElement, refRow); 
            else 
                parent.AppendChild( rowElement );
 
            FixNestedChildren(row, rowElement);
        }

        // Promote the rowElemChild node/region after prevSibling node (as the next sibling) 
        private void PromoteChild( XmlNode child, XmlNode prevSibling ) {
            // It makes no sense to move rowElemChild on the same level 
            Debug.Assert( child.ParentNode != prevSibling.ParentNode ); 
            // prevSibling must have a parent, since we want to add a sibling to it
            Debug.Assert( prevSibling.ParentNode != null ); 
            Debug.Assert( IsFoliationEnabled == false );
            Debug.Assert( IgnoreXmlEvents == true );
            // Should not insert after docElem node
            Debug.Assert( prevSibling != this.DocumentElement ); 

            if ( child.ParentNode != null ) 
                child.ParentNode.RemoveChild( child ); 

            Debug.Assert( child.ParentNode == null ); 
            prevSibling.ParentNode.InsertAfter( child, prevSibling );
        }

        // Promote child regions under parent as next siblings of parent 
        private void PromoteInnerRegions( XmlNode parent ) {
            Debug.Assert( parent != null ); 
            Debug.Assert( parent.NodeType != XmlNodeType.Attribute );   // We need to get get the grand-parent region 
            Debug.Assert( parent != DocumentElement );                  // We cannot promote children of the DocumentElement
 
            XmlNode prevSibling = parent;
            XmlBoundElement parentRegionRowElem;
            mapper.GetRegion( parent.ParentNode, out parentRegionRowElem );
 
            TreeIterator iter = new TreeIterator( parent );
            bool fMore = iter.NextRowElement(); 
            while ( fMore ) { 
                Debug.Assert( iter.CurrentNode is XmlBoundElement && ((XmlBoundElement)(iter.CurrentNode)).Row != null );
                XmlBoundElement rowElemChild = (XmlBoundElement)(iter.CurrentNode); 
                fMore = iter.NextRightRowElement();
                PromoteChild( rowElemChild, prevSibling );
                SetNestedParentRegion( rowElemChild, parentRegionRowElem );
            } 
        }
 
        private void PromoteNonValueChildren( XmlNode parent ) { 
            Debug.Assert( parent != null );
            XmlNode prevSibling = parent; 
            XmlNode child = parent.FirstChild;
            bool bTextLikeNode = true;
            XmlNode nextSibling = null;
            while ( child != null ) { 
                nextSibling = child.NextSibling;
                if (!bTextLikeNode || !IsTextLikeNode(child)) { 
                    bTextLikeNode = false; 
                    nextSibling = child.NextSibling;
                    PromoteChild( child, prevSibling ); 
                    prevSibling = child;
                }
                child = nextSibling;
            } 
        }
 
        private void RemoveInitialTextNodes( XmlNode node ) { 
            while ( node != null && IsTextLikeNode( node ) ) {
                XmlNode sibling = node.NextSibling; 
                node.ParentNode.RemoveChild( node );
                node = sibling;
            }
        } 

        private void ReplaceInitialChildText( XmlNode parent, string value ) { 
            XmlNode n = parent.FirstChild; 

            // don't consider whitespace when replacing initial text 
            while ( n != null && n.NodeType == XmlNodeType.Whitespace )
                n = n.NextSibling;

            if ( n != null ) { 
                if ( n.NodeType == XmlNodeType.Text )
                    n.Value = value; 
                else 
                    n = parent.InsertBefore( CreateTextNode( value ), n );
                RemoveInitialTextNodes( n.NextSibling ); 
            }
            else {
                parent.AppendChild( CreateTextNode( value ) );
            } 
        }
 
        internal XmlNode SafeFirstChild( XmlNode n ) { 
            XmlBoundElement be = n as XmlBoundElement;
            if ( be != null ) 
                return be.SafeFirstChild;
            else
                //other type of node should be already foliated.
                return n.FirstChild; 
        }
 
        internal XmlNode SafeNextSibling( XmlNode n ) { 
            XmlBoundElement be = n as XmlBoundElement;
            if ( be != null ) 
                return be.SafeNextSibling;
            else
                //other type of node should be already foliated.
                return n.NextSibling; 
        }
 
        internal XmlNode SafePreviousSibling( XmlNode n ) { 
            XmlBoundElement be = n as XmlBoundElement;
            if ( be != null ) 
                return be.SafePreviousSibling;
            else
                //other type of node should be already foliated.
                return n.PreviousSibling; 
        }
 
        internal static void SetRowValueToNull( DataRow row, DataColumn col ) { 
            Debug.Assert( col.ColumnMapping != MappingType.Hidden );
            Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 

            //
            if ( ! ( row.IsNull( col ) ) )
                row[ col ] = Convert.DBNull; 
        }
 
        internal static void SetRowValueFromXmlText( DataRow row, DataColumn col, string xmlText ) { 
            Debug.Assert( xmlText != null );
            Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 
            object oVal;
            try {
                oVal = col.ConvertXmlToObject( xmlText );
                // This func does not set the field value to null - call SetRowValueToNull in order to do so 
                Debug.Assert( oVal != null && ! ( oVal is DBNull ) );
            } 
            catch (Exception e) { 
                //
                if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) { 
                        throw;
                }
                // Catch data-type errors and set ROM to Unspecified value
                SetRowValueToNull( row, col ); 
                return;
            } 
 
            if ( ! oVal.Equals( row[col] ) )
                row[ col ] = oVal; 
        }

        private void SynchronizeRowFromRowElement( XmlBoundElement rowElement ) {
            SynchronizeRowFromRowElement( rowElement, null ); 
        }
        // [....] row fields w/ values from rowElem region. 
        // If rowElemList is != null, all subregions of rowElem are appended to it. 
        private void SynchronizeRowFromRowElement( XmlBoundElement rowElement, ArrayList rowElemList ) {
            DataRow row = rowElement.Row; 
            Debug.Assert( row != null );

            // No synchronization needed for deleted rows
            if ( row.RowState == DataRowState.Deleted ) 
                return;
 
            row.BeginEdit(); 
#if DEBUG
            try { 
#endif
                SynchronizeRowFromRowElementEx( rowElement, rowElemList );
#if DEBUG
            } 
            catch {
                // We should not get any exceptions because we always handle data-type conversion 
                Debug.Assert( false ); 
                throw;
            } 
#endif
#if DEBUG
            try {
#endif 
                row.EndEdit();
#if DEBUG 
            } 
            catch {
                // We should not get any exceptions because DataSet.EnforceConstraints should be always off 
                //
                Debug.Assert( false );
                throw;
            } 
#endif
        } 
        private void SynchronizeRowFromRowElementEx( XmlBoundElement rowElement, ArrayList rowElemList ) { 
            Debug.Assert( rowElement != null );
            Debug.Assert( rowElement.Row != null ); 
            Debug.Assert( this.DataSet.EnforceConstraints == false );

            DataRow row = rowElement.Row;
            Debug.Assert( row != null ); 
            DataTable table = row.Table;
 
            // if not foliated, already synch'd 
//            if ( !IsFoliated(rowElement) )
//                return; 
            //Debug.Assert( IsFoliated(rowElement) ); // If foliated we should not get the event (should be handled directly by DataPointer)

            Hashtable foundColumns = new Hashtable();
            string xsi_attrVal = string.Empty; 

            RegionIterator iter = new RegionIterator( rowElement ); 
            bool fMore; 
            // If present, fill up the TextOnly column
            DataColumn column = GetTextOnlyColumn( row ); 
            if ( column != null ) {
                foundColumns[column] = column;
                string value;
                fMore = iter.NextInitialTextLikeNodes( out value ); 
                if ( value.Length == 0 && ( ( (xsi_attrVal = rowElement.GetAttribute(XSI_NIL) ) == "1" ) || xsi_attrVal == "true" ) )
                    row[column] = Convert.DBNull; 
                else 
                    SetRowValueFromXmlText( row, column, value );
            } 
            else
                fMore = iter.Next();

            // Fill up the columns mapped to an element 
            while ( fMore ) {
                XmlElement e = iter.CurrentNode as XmlElement; 
                if ( e == null ) { 
                    fMore = iter.Next();
                    continue; 
                }

                XmlBoundElement be = e as XmlBoundElement;
                if ( be != null && be.Row != null ) { 
                    if ( rowElemList != null )
                        rowElemList.Add( e ); 
                    // Skip over sub-regions 
                    fMore = iter.NextRight();
                    continue; 
                }

                DataColumn c = mapper.GetColumnSchemaForNode( rowElement, e );
                if ( c != null ) { 
                    Debug.Assert( c.Table == row.Table );
                    if ( foundColumns[c] == null ) { 
                        foundColumns[c] = c; 
                        string value;
                        fMore = iter.NextInitialTextLikeNodes( out value ); 
                        if ( value.Length == 0 && ( ( (xsi_attrVal = e.GetAttribute(XSI_NIL) ) == "1" ) || xsi_attrVal == "true" ) )
                            row[c] = Convert.DBNull;
                        else
                            SetRowValueFromXmlText( row, c, value ); 
                        continue;
                    } 
                } 

                fMore = iter.Next(); 
            }

            //
            // Walk the attributes to find attributes that map to columns. 
            //
            foreach( XmlAttribute attr in rowElement.Attributes ) { 
                DataColumn c = mapper.GetColumnSchemaForNode( rowElement, attr ); 

                if ( c != null ) { 
                    if ( foundColumns[c] == null ) {
                        foundColumns[c] = c;
                        SetRowValueFromXmlText( row, c, attr.Value );
                    } 
                }
            } 
 
            // Null all columns values that aren't represented in the tree
            foreach( DataColumn c in row.Table.Columns ) { 
                if ( foundColumns[c] == null && !IsNotMapped(c) ) {
                    if (!c.AutoIncrement)
                        SetRowValueToNull( row, c );
                    else 
                        c.Init(row.tempRecord);
                } 
            } 
        }
 
        private void UpdateAllColumns( DataRow row, XmlBoundElement rowElement ) {
            foreach( DataColumn c in row.Table.Columns ) {
                OnColumnValueChanged( row, c, rowElement );
            } 
        }
 
        ///  
        ///    
        ///       Initializes a new instance of the XmlDataDocument class. 
        ///    
        /// 
        public XmlDataDocument(): base(new XmlDataImplementation()) {
            Init(); 
            AttachDataSet( new DataSet() );
            this.dataSet.EnforceConstraints = false; 
        } 

        ///  
        ///    
        ///       Initializes a new instance of the XmlDataDocument class with the specified
        ///       DataSet.
        ///     
        /// 
        public XmlDataDocument( DataSet dataset ): base(new XmlDataImplementation()) { 
            Init( dataset ); 
        }
 
        internal XmlDataDocument( XmlImplementation imp ) : base( imp ) {
        }

        private void Init() { 
            this.pointers = new Hashtable();
            this.countAddPointer = 0; 
            this.columnChangeList = new ArrayList(); 
            this.ignoreDataSetEvents = false;
            this.isFoliationEnabled = true; 
            this.optimizeStorage = true;
            this.fDataRowCreatedSpecial = false;
            autoFoliationState = ElementState.StrongFoliation;
            fAssociateDataRow = true; //this needs to be true for newly created elements should have associated datarows 
            mapper = new DataSetMapper();
            this.foliationLock = new object(); 
            this.ignoreXmlEvents = true; 
            this.attrXml = CreateAttribute( "xmlns", "xml", XPathNodePointer.s_strReservedXmlns );
            this.attrXml.Value = XPathNodePointer.s_strReservedXml; 
            this.ignoreXmlEvents = false;
        }

        private void Init( DataSet ds ) { 
            if ( ds == null )
                throw new ArgumentException(Res.GetString(Res.DataDom_DataSetNull)); 
            Init(); 
            if ( ds.FBoundToDocument )
                throw new ArgumentException( Res.GetString(Res.DataDom_MultipleDataSet) ); 
            ds.FBoundToDocument = true;
            this.dataSet = ds;
            Bind(true);
        } 

        private bool IsConnected( XmlNode node ) { 
            while ( true ) { 
                if ( node == null )
                    return false; 
                if ( node == this )
                    return true;

                XmlAttribute attr = node as XmlAttribute; 
                if ( attr != null )
                    node = attr.OwnerElement; 
                else 
                    node = node.ParentNode;
            } 
        }
        private bool IsRowLive( DataRow row ) {
            return ( row.RowState & ( DataRowState.Added | DataRowState.Unchanged | DataRowState.Modified ) ) != 0;
        } 
        private static void SetNestedParentRow( DataRow childRow, DataRow parentRow ) {
            DataRelation rel = GetNestedParentRelation( childRow ); 
            //we should not set this row's parentRow if the table doesn't match. 
            if ( rel != null ) {
                if ( parentRow == null || rel.ParentKey.Table != parentRow.Table ) 
                    childRow.SetParentRow( null, rel );
                else
                   childRow.SetParentRow( parentRow, rel );
            } 
        }
 
        // A node (node) was inserted into the main tree (connected) from oldParent==null state 
        private void OnNodeInsertedInTree( XmlNode node ) {
            XmlBoundElement be; 
            ArrayList rowElemList = new ArrayList();
            if ( mapper.GetRegion( node, out be ) ) {
                //
                if ( be == node ) { 
                    OnRowElementInsertedInTree( be, rowElemList );
                } 
                else { 
                    OnNonRowElementInsertedInTree( node, be, rowElemList );
                } 
            }
            else {
                // We only need to [....] the embedded sub-regions
                TreeIterator iter = new TreeIterator( node ); 
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() )
                    rowElemList.Add( iter.CurrentNode ); 
            } 

            // Process subregions, so they make transition from disconnected to connected tree 
            while ( rowElemList.Count > 0 ) {
                Debug.Assert(rowElemList[0] != null && rowElemList[0] is XmlBoundElement);
                XmlBoundElement subRowElem = (XmlBoundElement)(rowElemList[0]);
                rowElemList.RemoveAt( 0 ); 
                // Expect rowElem to have a DataTable schema, since it is a sub-region
                Debug.Assert( subRowElem != null ); 
                OnRowElementInsertedInTree( subRowElem, rowElemList ); 
            }
 
            // Assert that all sub-regions are assoc w/ "live" rows
            AssertLiveRows( node );
        }
        // "node" was inserting into a disconnected tree from oldParent==null state 
        private void OnNodeInsertedInFragment( XmlNode node ) {
            XmlBoundElement be; 
            if ( mapper.GetRegion( node, out be ) ) { 
                if ( be == node ) {
                    Debug.Assert( ! IsRowLive( be.Row ) ); 
                    SetNestedParentRegion( be );
                }
                else {
                    ArrayList rowElemList = new ArrayList(); 
                    OnNonRowElementInsertedInFragment( node, be, rowElemList );
                    // Set nested parent for the 1st level subregions (they should already be associated w/ Deleted or Detached rows) 
                    while ( rowElemList.Count > 0 ) { 
                        Debug.Assert(rowElemList[0] != null && rowElemList[0] is XmlBoundElement);
                        XmlBoundElement subRowElem = (XmlBoundElement)(rowElemList[0]); 
                        rowElemList.RemoveAt( 0 );
                        SetNestedParentRegion( subRowElem, be );
                    }
                } 

                // Check to make sure all sub-regions are disconnected 
                AssertNonLiveRows( node ); 

                return; 
            }

            // Nothing to do, since the node belongs to no region
 
            // Check to make sure all sub-regions are disconnected
            AssertNonLiveRows( node ); 
        } 

        // A row-elem was inserted into the connected tree (connected) from oldParent==null state 
        private void OnRowElementInsertedInTree( XmlBoundElement rowElem, ArrayList rowElemList ) {
            Debug.Assert( rowElem.Row != null );

            DataRow row = rowElem.Row; 
            DataRowState rowState = row.RowState;
 
            switch( rowState ) { 
            case DataRowState.Detached:
#if DEBUG 
                try {
                    Debug.Assert( row.Table.DataSet.EnforceConstraints == false );
#endif
                    row.Table.Rows.Add( row ); 
                    SetNestedParentRegion( rowElem );
#if DEBUG 
                } 
                catch {
                    // We should not get any exceptions here 
                    Debug.Assert( false );
                    throw;
                }
#endif 
                // Add all sub-regions to the list if the caller needs this
                if ( rowElemList != null ) { 
                    RegionIterator iter = new RegionIterator( rowElem ); 
                    for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() )
                        rowElemList.Add( iter.CurrentNode ); 
                }
                break;
            case DataRowState.Deleted:
#if DEBUG 
                try {
                    Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 
#endif 
                    // Change the row status to be alive (unchanged)
                    row.RejectChanges(); 
                    // Set ROM from XML
                    SynchronizeRowFromRowElement( rowElem, rowElemList );
                    // Set nested parent data row according to where is the row positioned in the tree
                    SetNestedParentRegion( rowElem ); 
#if DEBUG
                } 
                catch { 
                    // We should not get any exceptions here
                    Debug.Assert( false ); 
                    throw;
                }
#endif
                break; 
            default:
                // Handle your case above 
                // 
                Debug.Assert( false );
                break; 
            }
            Debug.Assert( IsRowLive( rowElem.Row ) );
        }
 
        // Disconnect the DataRow associated w/ the rowElem region
        private void EnsureDisconnectedDataRow( XmlBoundElement rowElem ) { 
            Debug.Assert( rowElem.Row != null ); 

            DataRow row = rowElem.Row; 
            DataRowState rowState = row.RowState;

            switch( rowState ) {
            case DataRowState.Detached: 
#if DEBUG
                try { 
                    Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 
#endif
                    SetNestedParentRegion( rowElem ); 
#if DEBUG
                }
                catch {
                    // We should not get any exceptions here 
                    Debug.Assert( false );
                    throw; 
                } 
#endif
                break; 

            case DataRowState.Deleted:
                // Nothing to do: moving a region associated w/ a deleted row to another disconnected tree is a NO-OP.
                break; 

            case DataRowState.Unchanged: 
            case DataRowState.Modified: 
                EnsureFoliation( rowElem, ElementState.WeakFoliation );
                row.Delete(); 
                break;

            case DataRowState.Added:
                EnsureFoliation( rowElem, ElementState.WeakFoliation ); 
                row.Delete();
                SetNestedParentRegion( rowElem ); 
                break; 

            default: 
                // Handle your case above
                //
                Debug.Assert( false );
                break; 
            }
 
            Debug.Assert( ! IsRowLive( rowElem.Row ) ); 
        }
 

        // A non-row-elem was inserted into the connected tree (connected) from oldParent==null state
        private void OnNonRowElementInsertedInTree( XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList ) {
            // non-row-elem is beeing inserted 
            DataRow row = rowElement.Row;
            // Region should already have an associated data row (otherwise how was the original row-elem inserted ?) 
            Debug.Assert( row != null ); 
            SynchronizeRowFromRowElement( rowElement );
            if ( rowElemList != null ) { 
                TreeIterator iter = new TreeIterator( node );
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() )
                    rowElemList.Add( iter.CurrentNode );
            } 
        }
 
        // A non-row-elem was inserted into disconnected tree (fragment) from oldParent==null state (i.e. was disconnected) 
        private void OnNonRowElementInsertedInFragment( XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList ) {
            // non-row-elem is beeing inserted 
            DataRow row = rowElement.Row;
            // Region should already have an associated data row (otherwise how was the original row-elem inserted ?)
            Debug.Assert( row != null );
            // Since oldParent == null, the only 2 row states should have been Detached or Deleted 
            Debug.Assert( row.RowState == DataRowState.Detached || row.RowState == DataRowState.Deleted );
 
            if ( row.RowState == DataRowState.Detached ) 
                SynchronizeRowFromRowElementEx( rowElement, rowElemList );
            // Nothing to do if the row is deleted (there is no [....]-ing from XML to ROM for deleted rows) 
        }

        private void SetNestedParentRegion( XmlBoundElement childRowElem ) {
            Debug.Assert( childRowElem.Row != null ); 

            XmlBoundElement parentRowElem; 
            mapper.GetRegion( childRowElem.ParentNode, out parentRowElem ); 
            SetNestedParentRegion( childRowElem, parentRowElem );
        } 
        private void SetNestedParentRegion( XmlBoundElement childRowElem, XmlBoundElement parentRowElem ) {
            DataRow childRow = childRowElem.Row;
            if ( parentRowElem == null ) {
                SetNestedParentRow( childRow, null ); 
                return;
            } 
 
            DataRow parentRow = parentRowElem.Row;
            Debug.Assert( parentRow != null ); 
            // We should set it only if there is a nested relationship between this child and parent regions
            DataRelation [] relations = childRow.Table.NestedParentRelations;
            if (relations.Length != 0 && relations[0].ParentTable == parentRow.Table ) // just backward compatable
                // 
                SetNestedParentRow( childRow, parentRow );
            else 
                SetNestedParentRow( childRow, null ); 
        }
 
        internal static bool IsTextNode( XmlNodeType nt ) {
            switch( nt ) {
                case XmlNodeType.Text:
                case XmlNodeType.CDATA: 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace: 
                    return true; 
                default:
                    return false; 
            }
        }

        /* 
        internal static bool IsWhiteSpace(char ch) {
            switch ( ch ) { 
                case '\u0009' : 
                case '\u000a' :
                case '\u000d' : 
                case '\u0020' :
                    return true;
                default :
                    return false; 
            }
        } 
 
        internal static bool IsOnlyWhitespace( string str ) {
            if (str != null) { 
                for (int index = 0; index < str.Length; index ++) {
                    if (! IsWhiteSpace(str[index]))
                        return false;
                } 
            }
            return true; 
        } 
        */
 
        /// 
        ///    [To be supplied.]
        /// 
        protected override XPathNavigator CreateNavigator(XmlNode node) { 
            Debug.Assert( node.OwnerDocument == this || node == this );
            if ( XPathNodePointer.xmlNodeType_To_XpathNodeType_Map[(int)(node.NodeType)] == -1 ) 
                return null; 
            if ( IsTextNode( node.NodeType ) ) {
                XmlNode parent = node.ParentNode; 
                if ( parent != null && parent.NodeType == XmlNodeType.Attribute )
                    return null;
                else {
#if DEBUG 
                    //if current node is a text node, its parent node has to be foliated
                    XmlBoundElement be = node.ParentNode as XmlBoundElement; 
                    if ( be != null ) 
                        Debug.Assert( be.IsFoliated );
#endif 
                    XmlNode prevSib = node.PreviousSibling;
                    while ( prevSib != null && IsTextNode( prevSib.NodeType ) ) {
                        node = prevSib;
                        prevSib = SafePreviousSibling( node ); 
                    }
                } 
            } 
            return new DataDocumentXPathNavigator( this, node );
        } 

        [System.Diagnostics.Conditional("DEBUG")]
        private void AssertLiveRows( XmlNode node ) {
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled = false;
            try { 
                XmlBoundElement rowElement = node as XmlBoundElement; 
                if ( rowElement != null && rowElement.Row != null )
                    Debug.Assert( IsRowLive( rowElement.Row ) ); 
                TreeIterator iter = new TreeIterator( node );
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement() ) {
                    rowElement = iter.CurrentNode as XmlBoundElement;
                    Debug.Assert( rowElement.Row != null ); 
                    Debug.Assert( IsRowLive( rowElement.Row ) );
                } 
            } 
            finally {
                IsFoliationEnabled = wasFoliationEnabled; 
            }
        }
        [System.Diagnostics.Conditional("DEBUG")]
        private void AssertNonLiveRows( XmlNode node ) { 
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false; 
            try { 
                XmlBoundElement rowElement = node as XmlBoundElement;
                if ( rowElement != null && rowElement.Row != null ) 
                    Debug.Assert( ! IsRowLive( rowElement.Row ) );
                TreeIterator iter = new TreeIterator( node );
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement() ) {
                    rowElement = iter.CurrentNode as XmlBoundElement; 
                    Debug.Assert( rowElement.Row != null );
                    Debug.Assert( ! IsRowLive( rowElement.Row ) ); 
                } 
            }
            finally { 
                IsFoliationEnabled = wasFoliationEnabled;
            }
        }
 
        public override XmlElement GetElementById( string elemId ) {
            throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_GetElementById ) ); 
        } 
        public override XmlNodeList GetElementsByTagName(string name) {
            // Retrieving nodes from the returned nodelist may cause foliation which causes new nodes to be created, 
            // so the System.Xml iterator will throw if this happens during iteration. To avoid this, foliate everything
            // before iteration, so iteration will not cause foliation (and as a result of this, creation of new nodes).
            XmlNodeList tempNodeList = base.GetElementsByTagName(name);
 
            int tempint = tempNodeList.Count;
            return tempNodeList; 
        } 

// Webdata 103397 

//  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
//  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
// so in order to maintain backward compatability, we reorder the copy of the datatable collection and use it 
        private DataTable[] OrderTables(DataSet ds) {
 
            DataTable[] retValue = null; 
            if (ds == null ||ds.Tables.Count == 0) {
                retValue = new DataTable[0]; 
            }
            else if (TablesAreOrdered(ds)) {
                retValue =  new DataTable[ds.Tables.Count];
                ds.Tables.CopyTo(retValue, 0); 
 		// XDD assumes PArent table exist before its child, if it does not we wont be handle the case
		// same as Everett 
            } 

            if (null == retValue) { 
                retValue =  new DataTable[ds.Tables.Count];
                List tableList = new List();
                // first take the root tables that have no parent
                foreach(DataTable dt in ds.Tables) { 
                    if (dt.ParentRelations.Count == 0) {
                        tableList.Add(dt); 
                    } 
                }
 
                if (tableList.Count > 0) { // if we have some  table inside;
                    foreach(DataTable dt in ds.Tables) {
                        if (IsSelfRelatedDataTable(dt)) {
                            tableList.Add(dt); 
                        }
                    } 
                    for(int readPos = 0 ; readPos < tableList.Count; readPos ++) { 
                        Debug.Assert(tableList[readPos] != null, "Temp Array is not supposed to reach to null");
                        foreach(DataRelation r in  tableList[readPos].ChildRelations) { 
                            DataTable childTable = r.ChildTable;
                            if (!tableList.Contains(childTable))
                                tableList.Add(childTable);
                        } 
                    }
                    tableList.CopyTo(retValue); 
 
                }
                else {//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 
                    ds.Tables.CopyTo(retValue, 0);
                }
            }
            return retValue; 
        }
        private bool IsSelfRelatedDataTable(DataTable rootTable) { 
            List tableList = new List(); 
            bool retValue = false;
            foreach(DataRelation r in  rootTable.ChildRelations) { 
                DataTable childTable = r.ChildTable;
                if (childTable == rootTable) {
                    retValue = true;
                    break; 
                }
                else if (!tableList.Contains(childTable)) { 
                    tableList.Add(childTable); 
                }
            } 
            if (!retValue) {
                for(int counter = 0 ; counter < tableList.Count; counter++) {
                    foreach(DataRelation r in  tableList[counter].ChildRelations) {
                        DataTable childTable = r.ChildTable; 
                        if (childTable == rootTable) {
                            retValue = true; 
                            break; 
                        }
                        else if (!tableList.Contains(childTable)) { 
                            tableList.Add(childTable);
                        }
                    }
                    if (retValue){ 
                        break;
                    } 
                } 
            }
            return retValue; 
        }
        private bool TablesAreOrdered(DataSet ds) {
            foreach(DataTable dt in ds.Tables){
                if (dt.Namespace != ds.Namespace) { 
                    return false;
                } 
            } 
            return true;
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
// [....] 
//----------------------------------------------------------------------------- 
namespace System.Xml {
    using System; 
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data; 
    using System.Diagnostics;
    using System.ComponentModel; 
    using System.Xml.XPath; 

    ///  
    ///    
    ///       Represents an entire document. An XmlDataDocument can contain XML
    ///       data or relational data (DataSet).
    ///     
    /// 
    [System.Security.Permissions.HostProtectionAttribute(Synchronization=true)] 
#if WINFSInternalOnly 
    internal
#else 
    public
#endif
    class XmlDataDocument: XmlDocument {
        DataSet dataSet; 

        DataSetMapper mapper; 
        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 
                                    // that there is at most one thread in AddPointer we are OK.
        int     countAddPointer;    // Approximate count of how many times AddPointer was called since the last time we removed the unused pointer objects from pointers hashtable. 
        ArrayList columnChangeList;
        DataRowState rollbackState;

        bool fBoundToDataSet;       // true if our permanent event listeners are registered to receive DataSet events 
        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.
        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). 
        bool ignoreXmlEvents;       // true if XML events should not be processed 
        bool ignoreDataSetEvents;   // true if DataSet events should not be processed
        bool isFoliationEnabled;    // true if we should create and reveal the virtual nodes, false if we should reveal only the physical stored nodes 
        bool optimizeStorage;       // false if we should only have foilated regions.
        ElementState autoFoliationState;    // When XmlBoundElement will foliate because of memeber functions, this will contain the foliation mode: usually this is
                                    // ElementState.StrongFoliation, however when foliation occurs due to DataDocumentNavigator operations (InsertNode for example),
                                    // it it usually ElementState.WeakFoliation 
        bool fAssociateDataRow;     // if true, CreateElement will create and associate data rows w/ the newly created XmlBoundElement.
                                    // If false, then CreateElement will just create the XmlBoundElement nodes. This is usefull for Loading case, 
                                    // when CreateElement is called by DOM. 
        object foliationLock;
        internal const string XSI_NIL = "xsi:nil"; 
        internal const string XSI = "xsi";
        private bool bForceExpandEntity = false;
        internal XmlAttribute attrXml = null;
        internal bool bLoadFromDataSet = false; 
        internal bool bHasXSINIL = false;
 
        internal void AddPointer( IXmlDataVirtualNode pointer ) { 
            Debug.Assert( pointers.ContainsValue(pointer) == false );
            lock ( pointers ) { 
                countAddPointer++;
                if ( countAddPointer >= 5 ) {   // 5 is choosed to be small enough to not affect perf, but high enough so we will not scan all the time
                    ArrayList al = new ArrayList();
                    foreach( DictionaryEntry entry in pointers ) { 
                        IXmlDataVirtualNode temp = (IXmlDataVirtualNode)(entry.Value);
                        Debug.Assert( temp != null ); 
                        if ( ! temp.IsInUse() ) 
                            al.Add( temp );
                    } 
                    for (int i = 0; i < al.Count; i++ ) {
                        pointers.Remove( al[ i ] );
                    }
                    countAddPointer = 0; 
                }
                pointers[pointer] = pointer; 
            } 
        }
 
        [System.Diagnostics.Conditional("DEBUG")]
        internal void AssertPointerPresent( IXmlDataVirtualNode pointer ) {
#if DEBUG
                object val = pointers[pointer]; 
                if ( val != ( object ) pointer )
                    Debug.Assert( false ); 
#endif 
        }
        // This function attaches the DataSet to XmlDataDocument 
        // We also register a special listener (OnDataRowCreatedSpecial) to DataSet, so we know when we should setup all regular listeners (OnDataRowCreated, OnColumnChanging, etc).
        // We need to do this because of the following scenario:
        //  - XmlDataDocument doc = new XmlDataDocument();
        //  - DataSet ds = doc.DataSet;     // doc.DataSet creates a data-set, however does not sets-up the regular listeners. 
        //  - ds.ReadXmlSchema();           // since there are regular listeners in doc that track ds schema changes, doc does not know about the new tables/columns/etc
        //  - ds.ReadXmlData();             // ds is now filled, however doc has no content (since there were no listeners for the new created DataRow's) 
        // 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 
        // (we rely on the fact that DataRowCreated is a DataSet wide event, rather than a DataTable event)
        private void AttachDataSet( DataSet ds ) { 
            // You should not have already an associated dataset
            Debug.Assert( dataSet == null );
            Debug.Assert( ds != null );
            if ( ds.FBoundToDocument ) 
                throw new ArgumentException( Res.GetString(Res.DataDom_MultipleDataSet) );
            ds.FBoundToDocument = true; 
            dataSet = ds; 
            // Register the special listener to catch the first DataRow event(s)
            BindSpecialListeners(); 
        }

        // after loading, all detached DataRows are synchronized with the xml tree and inserted to their tables
        // or after setting the innerxml, synchronize the rows and if created new and detached, will be inserted. 
        internal void SyncRows( DataRow parentRow, XmlNode node, bool fAddRowsToTable) {
            XmlBoundElement be = node as XmlBoundElement; 
            if ( be != null ) { 
                DataRow r = be.Row;
                if (r!=null && be.ElementState == ElementState.Defoliated) 
                    return; //no need of syncRow

                if ( r != null ) {
                    // get all field values. 
                    SynchronizeRowFromRowElement( be );
 
                    // defoliate if possible 
                    be.ElementState = ElementState.WeakFoliation;
                    DefoliateRegion( be ); 

                    if ( parentRow != null )
                        SetNestedParentRow( r, parentRow );
                    if ( fAddRowsToTable && r.RowState == DataRowState.Detached ) 
                        r.Table.Rows.Add( r );
                    parentRow = r; 
                } 
            }
 
            // Attach all rows from children nodes
            for ( XmlNode child = node.FirstChild; child != null; child = child.NextSibling )
            	SyncRows( parentRow, child, fAddRowsToTable );
        } 

        // All detached DataRows are synchronized with the xml tree and inserted to their tables. 
        // Synchronize the rows and if created new and detached, will be inserted. 
        internal void SyncTree( XmlNode node) {
            XmlBoundElement be = null; 
            mapper.GetRegion( node, out be ) ;
            DataRow parentRow = null;
            bool fAddRowsToTable = IsConnected(node) ;
 
            if ( be != null ) {
                DataRow r = be.Row; 
                if (r!=null && be.ElementState == ElementState.Defoliated) 
                    return; //no need of syncRow
 
                if ( r != null ) {
                    // get all field values.
                    SynchronizeRowFromRowElement( be );
 
                    // defoliation will not be done on the node which is not RowElement, in case of node is externally being used
                    if ( node == be ) { 
                         // defoliate if possible 
                         be.ElementState = ElementState.WeakFoliation;
                         DefoliateRegion( be ); 
                    }

                    if ( fAddRowsToTable && r.RowState == DataRowState.Detached )
                        r.Table.Rows.Add( r ); 

                    parentRow = r; 
                } 
            }
 
            // Attach all rows from children nodes
            for ( XmlNode child = node.FirstChild; child != null; child = child.NextSibling )
            	SyncRows( parentRow, child, fAddRowsToTable );
        } 

        internal ElementState AutoFoliationState { 
            get { return this.autoFoliationState; } 
            set { this.autoFoliationState = value; }
        } 

        private void BindForLoad() {
            Debug.Assert( ignoreXmlEvents == true );
            ignoreDataSetEvents = true; 
            mapper.SetupMapping( this, dataSet );
            if ( dataSet.Tables.Count > 0 ) { 
                //at least one table 
                LoadDataSetFromTree( );
            } 
            BindListeners();
            ignoreDataSetEvents = false;
        }
 
        private void Bind( bool fLoadFromDataSet ) {
            // If we have a DocumentElement then it is illegal to call this func to load from data-set 
            Debug.Assert( DocumentElement == null || ! fLoadFromDataSet ); 

            ignoreDataSetEvents = true; 
            ignoreXmlEvents = true;

            // Do the mapping. This could be a successive mapping in case of this scenario: xd = XmlDataDocument( emptyDataSet ); xd.Load( "file.xml" );
            mapper.SetupMapping(this, dataSet); 

            if ( DocumentElement != null ) { 
                LoadDataSetFromTree(); 
                BindListeners();
            } 
            else if ( fLoadFromDataSet ) {
                this.bLoadFromDataSet = true;
                LoadTreeFromDataSet( DataSet );
                BindListeners(); 
            }
 
            ignoreDataSetEvents = false; 
            ignoreXmlEvents = false;
        } 

        internal void Bind( DataRow r, XmlBoundElement e ) {
            r.Element = e;
            e.Row = r; 
        }
 
        // Binds special listeners to catch the 1st data-row created. When the 1st DataRow is created, XmlDataDocument will automatically bind all regular listeners. 
        private void BindSpecialListeners() {
            Debug.Assert( fDataRowCreatedSpecial == false ); 
            Debug.Assert( fBoundToDataSet == false && fBoundToDocument == false );
            dataSet.DataRowCreated += new DataRowCreatedEventHandler( this.OnDataRowCreatedSpecial );
            fDataRowCreatedSpecial = true;
        } 
        private void UnBindSpecialListeners() {
            Debug.Assert( fDataRowCreatedSpecial == true ); 
            dataSet.DataRowCreated -= new DataRowCreatedEventHandler( this.OnDataRowCreatedSpecial ); 
            fDataRowCreatedSpecial = false;
        } 

        private void BindListeners() {
            BindToDocument();
            BindToDataSet(); 
        }
 
        private void BindToDataSet() { 
            // We could be already bound to DataSet in this scenario:
            //     xd = new XmlDataDocument( dataSetThatHasNoData ); xd.Load( "foo.xml" ); 
            // so we must not rebound again to it.
            if ( fBoundToDataSet ) {
                Debug.Assert( dataSet != null );
                return; 
            }
 
            // Unregister the DataRowCreatedSpecial notification 
            if ( fDataRowCreatedSpecial )
                UnBindSpecialListeners(); 

            dataSet.Tables.CollectionChanging += new CollectionChangeEventHandler( this.OnDataSetTablesChanging );
            dataSet.Relations.CollectionChanging += new CollectionChangeEventHandler( this.OnDataSetRelationsChanging );
            dataSet.DataRowCreated += new DataRowCreatedEventHandler( this.OnDataRowCreated ); 
            dataSet.PropertyChanging += new PropertyChangedEventHandler( this.OnDataSetPropertyChanging );
 
            //this is the hack for this release, should change it in the future 
            dataSet.ClearFunctionCalled += new DataSetClearEventhandler( this.OnClearCalled );
 
            if ( dataSet.Tables.Count > 0 ) {
                foreach( DataTable t in dataSet.Tables ) {
                    BindToTable( t );
                } 
            }
 
            foreach ( DataRelation rel in dataSet.Relations ) { 
                rel.PropertyChanging += new PropertyChangedEventHandler( this.OnRelationPropertyChanging );
            } 
            fBoundToDataSet = true;
        }

        private void BindToDocument() { 
            if ( !fBoundToDocument ) {
                NodeInserting +=  new XmlNodeChangedEventHandler( this.OnNodeInserting ) ; 
                NodeInserted +=  new XmlNodeChangedEventHandler( this.OnNodeInserted ) ; 
                NodeRemoving +=  new XmlNodeChangedEventHandler( this.OnNodeRemoving ) ;
                NodeRemoved +=  new XmlNodeChangedEventHandler( this.OnNodeRemoved ) ; 
                NodeChanging +=  new XmlNodeChangedEventHandler( this.OnNodeChanging ) ;
                NodeChanged +=  new XmlNodeChangedEventHandler( this.OnNodeChanged ) ;
                fBoundToDocument = true;
            } 
        }
 
        private void BindToTable( DataTable t ) { 
//            t.ColumnChanging  += new DataColumnChangeEventHandler( this.OnColumnChanging );
            t.ColumnChanged   += new DataColumnChangeEventHandler( this.OnColumnChanged ); 
            t.RowChanging     += new DataRowChangeEventHandler( this.OnRowChanging );
            t.RowChanged      += new DataRowChangeEventHandler( this.OnRowChanged );
            t.RowDeleting     += new DataRowChangeEventHandler( this.OnRowChanging);
            t.RowDeleted      += new DataRowChangeEventHandler( this.OnRowChanged ); 
            t.PropertyChanging += new PropertyChangedEventHandler( this.OnTablePropertyChanging );
            t.Columns.CollectionChanging += new CollectionChangeEventHandler( this.OnTableColumnsChanging ); 
 
            foreach( DataColumn col in t.Columns ) {
                // Hook column properties changes, so we can react properly to ROM changes. 
                col.PropertyChanging += new PropertyChangedEventHandler( this.OnColumnPropertyChanging );
            }
        }
 
        /// 
        ///     
        ///       Creates an element with the specified Prefix, LocalName, and 
        ///       NamespaceURI.
        ///     
        /// 
        public override XmlElement CreateElement( string prefix, string localName, string namespaceURI) {
            //
 
            // There are three states for the document:
            //  - 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 
            //    is created we should subscribe the permenent listeners. 
            //  - special listeners OFF, permanent listeners ON: this is when the data doc is loaded (from dataset or XML file) and synchronization takes place.
            //  - 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, 
            //    not based on listening to events.
#if DEBUG
            // Cannot have both special and permananent listeners ON
            if ( fDataRowCreatedSpecial ) 
                Debug.Assert( (fBoundToDataSet == false) && (fBoundToDocument == false) );
            // fBoundToDataSet and fBoundToDocument should have the same value 
            Debug.Assert( fBoundToDataSet ? fBoundToDocument : (! fBoundToDocument) ); 
#endif
            if ( prefix == null ) 
                prefix = String.Empty;
            if ( namespaceURI == null )
                namespaceURI = String.Empty;
 
            if ( ! fAssociateDataRow ) {
                // Loading state: create just the XmlBoundElement: the LoadTreeFromDataSet/LoadDataSetFromTree will take care of synchronization 
                return new XmlBoundElement( prefix, localName, namespaceURI, this ); 
            }
 
            // 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/
            // creation of this element
            EnsurePopulatedMode();
            Debug.Assert( fDataRowCreatedSpecial == false ); 

            // Loaded state: create a DataRow, this in turn will create and associate the XmlBoundElement, which we will return. 
            DataTable dt = mapper.SearchMatchingTableSchema( localName, namespaceURI ); 
            if ( dt != null ) {
                DataRow row = dt.CreateEmptyRow(); 
                // We need to make sure all fields are DBNull
                foreach( DataColumn col in dt.Columns ) {
                    if ( col.ColumnMapping != MappingType.Hidden )
                        SetRowValueToNull( row, col ); 
                }
                XmlBoundElement be = row.Element; 
                Debug.Assert( be != null ); 
                be.Prefix = prefix;
                return be; 
            }
            // No matching table schema for this element: just create the element
            return new XmlBoundElement( prefix, localName, namespaceURI, this );
        } 

        ///  
        ///    [To be supplied.] 
        /// 
        public override XmlEntityReference CreateEntityReference(String name) { 
            throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_EntRef ) );
        }

        ///  
        ///    Gets a DataSet that provides a relational representation of the data in this
        ///       XmlDataDocument. 
        ///  
        public DataSet DataSet {
            get { 
                return dataSet;
            }
        }
 
        private void DefoliateRegion( XmlBoundElement rowElem ) {
            // You must pass a row element (which s/b associated w/ a DataRow) 
            Debug.Assert( rowElem.Row != null ); 

            if ( !optimizeStorage ) 
                return;

            if ( rowElem.ElementState != ElementState.WeakFoliation )
                return; 

            if ( !mapper.IsRegionRadical( rowElem ) ) { 
                //Console.WriteLine("Region is not radical: "+rowElem.GetHashCode()); 
                return;
            } 

            //Console.WriteLine("Defoliating Region: " + rowElem.GetHashCode());

            bool saveIgnore = IgnoreXmlEvents; 
            IgnoreXmlEvents = true;
 
            rowElem.ElementState = ElementState.Defoliating; 

            try { 
                // drop all attributes
                rowElem.RemoveAllAttributes();

                XmlNode node = rowElem.FirstChild; 
                while ( node != null ) {
                    XmlNode next = node.NextSibling; 
 
                    XmlBoundElement be = node as XmlBoundElement;
                    if ( be != null && be.Row != null ) 
                        break;

                    // The node must be mapped to a column (since the region is radically structured)
                    Debug.Assert( mapper.GetColumnSchemaForNode( rowElem, node ) != null ); 
                    rowElem.RemoveChild( node );
 
                    node = next; 
                }
#if DEBUG 
                // All subsequent siblings must be sub-regions
                for ( ; node != null; node = node.NextSibling ) {
                    Debug.Assert( (node is XmlBoundElement) && (((XmlBoundElement)node).Row != null) );
                } 
#endif
 
                rowElem.ElementState = ElementState.Defoliated; 
            }
            finally { 
                IgnoreXmlEvents = saveIgnore;
            }
        }
 
        private XmlElement EnsureDocumentElement() {
            XmlElement docelem = DocumentElement; 
            if ( docelem == null ) { 
                string docElemName = XmlConvert.EncodeLocalName( this.DataSet.DataSetName );
                if ( docElemName == null || docElemName.Length == 0 ) 
                    docElemName = "Xml";
                string ns = this.DataSet.Namespace;
                if ( ns == null )
                    ns = String.Empty; 
                docelem = new XmlBoundElement( string.Empty, docElemName, ns, this );
                AppendChild( docelem ); 
            } 

            return docelem; 
        }
        private XmlElement EnsureNonRowDocumentElement() {
            XmlElement docElem = this.DocumentElement;
            if ( docElem == null ) 
                return EnsureDocumentElement();
 
            DataRow rowDocElem = GetRowFromElement( docElem ); 
            if ( rowDocElem == null )
                return docElem; 

            return DemoteDocumentElement();
        }
        private XmlElement DemoteDocumentElement() { 
            // Changes of Xml here should not affect ROM
            Debug.Assert( this.ignoreXmlEvents == true ); 
            // There should be no reason to call this function if docElem is not a rowElem 
            Debug.Assert( this.GetRowFromElement( this.DocumentElement ) != null );
 
            // Remove the DocumentElement and create a new one
            XmlElement oldDocElem = this.DocumentElement;
            RemoveChild( oldDocElem );
            XmlElement docElem = EnsureDocumentElement(); 
            docElem.AppendChild( oldDocElem );
            // We should have only one child now 
            Debug.Assert( docElem.LastChild == docElem.FirstChild ); 
            return docElem;
        } 
        // This function ensures that the special listeners are un-subscribed, the permanent listeners are subscribed and
        // CreateElement will attach DataRows to newly created XmlBoundElement.
        // It should be called when we have special listeners hooked and we need to change from the special-listeners mode to the
        // populated/permanenet mode where all listeners are corectly hooked up and the mapper is correctly set-up. 
        private void EnsurePopulatedMode() {
            // Unbind special listeners, bind permanent ones, setup the mapping, etc 
#if DEBUG 
            bool fDataRowCreatedSpecialOld = fDataRowCreatedSpecial;
            bool fAssociateDataRowOld = fAssociateDataRow; 
#endif
            if ( fDataRowCreatedSpecial ) {
                UnBindSpecialListeners();
                // If a special listener was ON, we should not have had an already set-up mapper or permanent listeners subscribed 
                Debug.Assert( ! mapper.IsMapped() );
                Debug.Assert( ! fBoundToDocument ); 
                Debug.Assert( ! fBoundToDataSet ); 

                mapper.SetupMapping( this, dataSet); 
                BindListeners();

                // CreateElement should now create associate DataRows w/ new XmlBoundElement nodes
                // We should do this ONLY if we switch from special listeners to permanent listeners. The reason is 
                // that DataDocumentNavigator wants to put XmlDataDocument in a batch mode, where CreateElement will just
                // create a XmlBoundElement (see DataDocumentNavigator.CloneTree) 
                fAssociateDataRow = true; 
            }
 
            Debug.Assert( fDataRowCreatedSpecial == false );
            Debug.Assert( mapper.IsMapped() );
            Debug.Assert( fBoundToDataSet && fBoundToDocument );
#if DEBUG 
            // In case we EnsurePopulatedMode was called on an already populated mode, we should NOT change fAssociateDataRow
            if ( fDataRowCreatedSpecialOld == false ) 
                Debug.Assert( fAssociateDataRowOld == fAssociateDataRow ); 
#endif
        } 

        // Move regions that are marked in ROM as nested children of row/rowElement as last children in XML fragment
        private void FixNestedChildren(DataRow row, XmlElement rowElement) {
            foreach( DataRelation dr in GetNestedChildRelations(row) ) { 
                foreach( DataRow r in row.GetChildRows(dr) ) {
                    XmlElement childElem = r.Element; 
                    // childElem can be null when we create XML from DataSet (XmlDataDocument( DataSet ) is called) and we insert rowElem of the parentRow before 
                    // we insert the rowElem of children rows.
                    if ( childElem != null ) { 
#if DEBUG
                        bool fIsChildConnected = IsConnected( childElem );
#endif
                        if ( childElem.ParentNode != rowElement ) { 
                            childElem.ParentNode.RemoveChild( childElem );
                            rowElement.AppendChild( childElem ); 
                        } 
#if DEBUG
                        // We should not have changed the connected/disconnected state of the node (since the row state did not change) 
                        Debug.Assert( fIsChildConnected == IsConnected( childElem ) );
                        Debug.Assert( IsRowLive( r ) ? IsConnected( childElem ) : ! IsConnected( childElem ) );
#endif
                    } 
                }
            } 
        } 

        // This function accepts node params that are not row-elements. In this case, calling this function is a no-op 
        internal void Foliate( XmlBoundElement node, ElementState newState ) {

            Debug.Assert( newState == ElementState.WeakFoliation || newState == ElementState.StrongFoliation );
#if DEBUG 
            // 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)
            // Do this only when we are not loading 
            if ( IsFoliationEnabled ) { 
                if( newState == ElementState.StrongFoliation && node.Row == null ) {
                    XmlBoundElement rowElem; 
                    ElementState rowElemState = ElementState.None;
                    if ( mapper.GetRegion( node, out rowElem ) ) {
                        rowElemState = rowElem.ElementState;
                        Debug.Assert( rowElemState == ElementState.StrongFoliation || rowElemState == ElementState.WeakFoliation ); 
                    }
                    // Add a no-op, so we can still debug in the assert fails 
 
#pragma warning disable 1717 // assignment to self
                    rowElemState = rowElemState; 
#pragma warning restore 1717
                }
            }
#endif 

            if ( IsFoliationEnabled ) { 
                if ( node.ElementState == ElementState.Defoliated ) { 
                    ForceFoliation( node, newState );
                } 
                else if ( node.ElementState == ElementState.WeakFoliation && newState == ElementState.StrongFoliation ) {
                    // Node must be a row-elem
                    Debug.Assert( node.Row != null );
                    node.ElementState = newState; 
                }
            } 
        } 

        private void Foliate( XmlElement element ) { 
            if ( element is XmlBoundElement )
                ((XmlBoundElement)element).Foliate( ElementState.WeakFoliation );
        }
 
        // Foliate rowElement region if there are DataPointers that points into it
        private void FoliateIfDataPointers( DataRow row, XmlElement rowElement ) { 
            if ( !IsFoliated( rowElement) && HasPointers( rowElement ) ) { 
                bool wasFoliationEnabled = IsFoliationEnabled;
                IsFoliationEnabled = true; 
                try {
                    Foliate( rowElement );
                }
                finally { 
                    IsFoliationEnabled = wasFoliationEnabled;
                } 
            } 
        }
 
        private void EnsureFoliation( XmlBoundElement rowElem, ElementState foliation ) {
            if ( rowElem.IsFoliated ) //perf reason, avoid unecessary lock.
                return;
            ForceFoliation( rowElem, foliation); 
        }
 
        private void ForceFoliation( XmlBoundElement node, ElementState newState ) { 
            lock ( this.foliationLock ) {
                if ( node.ElementState != ElementState.Defoliated ) 
                    // The region was foliated by an other thread while this thread was locked
                    return;

                // Node must be a row-elem associated w/ a non-deleted row 
                Debug.Assert( node.Row != null );
                Debug.Assert( node.Row.RowState != DataRowState.Deleted ); 
 
                node.ElementState = ElementState.Foliating;
 
                bool saveIgnore = IgnoreXmlEvents;
                IgnoreXmlEvents = true;

                try { 
                    XmlNode priorNode = null;
                    DataRow row = node.Row; 
 
                    // create new attrs & elements for row
                    // For detached rows: we are in [....] w/ temp values 
                    // For non-detached rows: we are in [....] w/ the current values
                    // For deleted rows: we never [....]
                    DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current;
                    foreach( DataColumn col in row.Table.Columns ) { 
                        if ( !IsNotMapped(col) ) {
                            object value = row[col, rowVersion]; 
 
                            if ( ! Convert.IsDBNull( value ) ) {
                                if ( col.ColumnMapping == MappingType.Attribute ) { 
                                    node.SetAttribute( col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml( value ) );
                                }
                                else {
                                    XmlNode newNode = null; 
                                    if ( col.ColumnMapping == MappingType.Element ) {
                                        newNode = new XmlBoundElement( string.Empty, col.EncodedColumnName, col.Namespace, this ); 
                                        newNode.AppendChild( CreateTextNode( col.ConvertObjectToXml( value ) ) ); 
                                        if ( priorNode != null ) {
                                            node.InsertAfter(newNode, priorNode); 
                                        }
                                        else if ( node.FirstChild != null ) {
                                            node.InsertBefore( newNode, node.FirstChild );
                                        } 
                                        else {
                                            node.AppendChild( newNode ); 
                                        } 
                                        priorNode = newNode;
                                    } 
                                    else {
                                        Debug.Assert( col.ColumnMapping == MappingType.SimpleContent );
                                        newNode = CreateTextNode( col.ConvertObjectToXml( value ) );
                                        if ( node.FirstChild != null ) 
                                            node.InsertBefore( newNode, node.FirstChild );
                                        else 
                                            node.AppendChild( newNode ); 
                                        if ( priorNode == null )
                                            priorNode = newNode; 
                                    }
                                }
                            }
                            else { 
                                if ( col.ColumnMapping == MappingType.SimpleContent ) {
                                    XmlAttribute attr = CreateAttribute( XSI, Keywords.XSI_NIL, Keywords.XSINS ); 
                                    attr.Value = Keywords.TRUE; 
                                    node.SetAttributeNode( attr );
                                    this.bHasXSINIL = true; 
                                }
                            }
                        }
                    } 
                }
                finally { 
                    IgnoreXmlEvents = saveIgnore; 
                    node.ElementState = newState;
                } 
                // update all live pointers
                OnFoliated( node );
            }
        } 

        //Determine best radical insert position for inserting column elements 
        private XmlNode GetColumnInsertAfterLocation( DataRow row, DataColumn col, XmlBoundElement rowElement ) { 
            XmlNode prev = null;
            XmlNode node = null; 

            // text only columns appear first
            if ( IsTextOnly( col ) )
                return null; 

            // insert location must be after free text 
            for ( node = rowElement.FirstChild; node != null; prev = node, node = node.NextSibling ) { 
                if ( !IsTextLikeNode( node ) )
                    break; 
            }

            for ( ; node != null; prev = node, node = node.NextSibling ) {
                // insert location must be before any non-element nodes 
                if ( node.NodeType != XmlNodeType.Element )
                    break; 
                XmlElement e = node as XmlElement; 

                // insert location must be before any non-mapped elements or separate regions 
                if ( mapper.GetRowFromElement( e ) != null )
                    break;

                object schema = mapper.GetColumnSchemaForNode( rowElement, node ); 
                if ( schema == null || !(schema is DataColumn) )
                    break; 
 
                // insert location must be before any columns logically after this column
                if ( ((DataColumn)schema).Ordinal > col.Ordinal ) 
                    break;
            }

            return prev; 
        }
 
        private ArrayList GetNestedChildRelations(DataRow row) { 
            ArrayList list = new ArrayList();
 
            foreach( DataRelation r in row.Table.ChildRelations ) {
                if ( r.Nested )
                    list.Add(r);
            } 

            return list; 
        } 

        private DataRow GetNestedParent(DataRow row) { 
            DataRelation relation = GetNestedParentRelation(row);
            if ( relation != null )
                return row.GetParentRow(relation);
            return null; 
        }
 
        private static DataRelation GetNestedParentRelation( DataRow row ) { 
            DataRelation [] relations = row.Table.NestedParentRelations;
            if (relations.Length == 0) 
                return null;
            return relations[0];
        }
 
        private DataColumn GetTextOnlyColumn( DataRow row ) {
#if DEBUG 
            { 
                // Make sure there is at most only one text column, and the text column (if present) is the one reported by row.Table.XmlText
                DataColumnCollection columns = row.Table.Columns; 
                int cCols = columns.Count;
                int cTextCols = 0;
                for ( int iCol = 0; iCol < cCols; iCol++ ) {
                    DataColumn c = columns[iCol]; 
                    if ( IsTextOnly( c ) ) {
                        Debug.Assert( c == row.Table.XmlText ); 
                        ++cTextCols; 
                    }
                } 
                Debug.Assert( cTextCols == 0 || cTextCols == 1 );
                if ( cTextCols == 0 )
                    Debug.Assert( row.Table.XmlText == null );
            } 
#endif
            return row.Table.XmlText; 
        } 

        ///  
        ///    
        ///       Retrieves the
        ///          DataRow associated with the specified XmlElement.
        ///        
        ///    
        public DataRow GetRowFromElement( XmlElement e ) { 
            return mapper.GetRowFromElement( e ); 
        }
 
        private XmlNode GetRowInsertBeforeLocation(DataRow row, XmlElement rowElement, XmlNode parentElement) {
            DataRow refRow = row;
            int i = 0;
            int pos; 

            // Find position 
            // int pos = row.Table.Rows[row]; 
            for (i = 0; i < row.Table.Rows.Count; i++)
                if (row == row.Table.Rows[i]) 
                    break;
            pos = i;

            DataRow parentRow = GetNestedParent(row); 
            for (i = pos + 1; i < row.Table.Rows.Count; i++) {
                refRow = row.Table.Rows[i]; 
                if (GetNestedParent(refRow) == parentRow && GetElementFromRow(refRow).ParentNode == parentElement) 
                    break;
            } 

            if (i < row.Table.Rows.Count)
                return GetElementFromRow(refRow);
            else 
                return(XmlNode) null;
        } 
 

        ///  
        ///    Retrieves
        ///       the XmlElement associated with the specified DataRow.
        /// 
        public XmlElement GetElementFromRow( DataRow r ) { 
            XmlBoundElement be = r.Element;
            // 
            Debug.Assert( be != null ); 
            return be;
        } 

        internal bool HasPointers( XmlNode node ) {
            while ( true ) {
                try { 
                    if ( pointers.Count > 0 ) {
                        object pointer = null; 
                        foreach( DictionaryEntry entry in pointers ) { 
                            pointer = entry.Value;
                            Debug.Assert( pointer != null ); 
                            if ( ((IXmlDataVirtualNode)pointer).IsOnNode( node ) )
                                return true;
                        }
                    } 
                    return false;
                } 
                catch (Exception e) { 
                    // This can happens only when some threads are creating navigators (thus modifying this.pointers) while other threads are in the foreach loop.
                    // Solution is to re-try HasPointers. 
                    //
                    if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) {
                        throw;
                    } 
                }
            } 
            //should never get to this point due to while (true) loop 
        }
 
        internal bool IgnoreXmlEvents {
            get { return ignoreXmlEvents;}
            set { ignoreXmlEvents = value;}
        } 

        internal bool IgnoreDataSetEvents { 
            get { return this.ignoreDataSetEvents; } 
            set { this.ignoreDataSetEvents = value; }
        } 

        private bool IsFoliated( XmlElement element ) {
            if ( element is XmlBoundElement ) {
                return((XmlBoundElement)element).IsFoliated; 
            }
 
            return true; 
        }
        private bool IsFoliated( XmlBoundElement be ) { 
            return be.IsFoliated;
        }

        internal bool IsFoliationEnabled { 
            get { return isFoliationEnabled; }
            set { isFoliationEnabled = value; } 
        } 

        // This creates a tree and synchronize ROM w/ the created tree. 
        // 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.
        // It takes advantage of the fAssociateDataRow flag for populated mode, which allows creation of XmlBoundElement w/o associating DataRow objects.
        internal XmlNode CloneTree( DataPointer other ) {
            EnsurePopulatedMode(); 

            bool oldIgnoreDataSetEvents = ignoreDataSetEvents; 
            bool oldIgnoreXmlEvents     = ignoreXmlEvents; 
            bool oldFoliationEnabled    = IsFoliationEnabled;
            bool oldAssociateDataRow    = fAssociateDataRow; 

            // Caller should ensure that the EnforceConstraints == false. See 60486 for more info about why this was changed from DataSet.EnforceConstraints = false to an assert.
            Debug.Assert( DataSet.EnforceConstraints == false );
            XmlNode newNode; 

            try { 
                ignoreDataSetEvents = true; 
                ignoreXmlEvents     = true;
                IsFoliationEnabled  = false; 
                fAssociateDataRow   = false;

                // Create the diconnected tree based on the other navigator
                newNode = CloneTreeInternal( other ); 
                Debug.Assert( newNode != null );
 
                // Synchronize DataSet from XML 
                LoadRows( null, newNode );
                SyncRows( null, newNode, false ); 
            }
            finally {
                ignoreDataSetEvents = oldIgnoreDataSetEvents;
                ignoreXmlEvents     = oldIgnoreXmlEvents; 
                IsFoliationEnabled  = oldFoliationEnabled;
                fAssociateDataRow   = oldAssociateDataRow; 
            } 
            return newNode;
        } 

        private XmlNode CloneTreeInternal( DataPointer other ) {
            Debug.Assert( ignoreDataSetEvents == true );
            Debug.Assert( ignoreXmlEvents == true ); 
            Debug.Assert( IsFoliationEnabled == false );
 
            // Create the diconnected tree based on the other navigator 
            XmlNode newNode = CloneNode( other );
 
            DataPointer dp = new DataPointer( other );
            try {
                dp.AddPointer();
                if (newNode.NodeType == XmlNodeType.Element) { 
                    int cAttributes = dp.AttributeCount;
                    for ( int i = 0; i < cAttributes; i++ ) { 
                        dp.MoveToOwnerElement(); 
                        if( dp.MoveToAttribute(i) ) {
                            newNode.Attributes.Append( (XmlAttribute)CloneTreeInternal(dp) ); 
                        }
                    }

                    dp.MoveTo( other ); 
                }
 
                // 
                for ( bool fMore = dp.MoveToFirstChild(); fMore; fMore = dp.MoveToNextSibling() )
                    newNode.AppendChild( CloneTreeInternal( dp ) ); 
            }
            finally {
                dp.SetNoLongerUse();
            } 

            return newNode; 
        } 

        ///  
        ///    [To be supplied.]
        /// 
        public override XmlNode CloneNode( bool deep ) {
            XmlDataDocument clone = (XmlDataDocument)(base.CloneNode(false)); 
            clone.Init(this.DataSet.Clone());
 
            clone.dataSet.EnforceConstraints = this.dataSet.EnforceConstraints; 
            Debug.Assert( clone.FirstChild == null );
            if ( deep ) { 
                DataPointer dp = new DataPointer( this, this );
                try {
                    dp.AddPointer();
                    for ( bool fMore = dp.MoveToFirstChild(); fMore; fMore = dp.MoveToNextSibling() ) { 
                        XmlNode cloneNode;
                        if ( dp.NodeType == XmlNodeType.Element ) 
                            cloneNode = clone.CloneTree( dp ); 
                        else
                            cloneNode = clone.CloneNode( dp ); 
                        clone.AppendChild( cloneNode );
                    }
                }
                finally{ 
                    dp.SetNoLongerUse();
                } 
            } 

            return clone; 
        }

        private XmlNode CloneNode( DataPointer dp ) {
            switch (dp.NodeType) { 
                //for the nodes without value and have no children
                case XmlNodeType.DocumentFragment: 
                    return this.CreateDocumentFragment(); 
                case XmlNodeType.DocumentType:
                    // 
                    return this.CreateDocumentType( dp.Name, dp.PublicId, dp.SystemId, dp.InternalSubset );
                case XmlNodeType.XmlDeclaration:
                    return this.CreateXmlDeclaration( dp.Version, dp.Encoding, dp.Standalone );
                //case XmlNodeType.Notation: -- notation should not be able to be accessed by XmlNavigator 
                    //return this.CreateNotation(dp.Name, dp.PublicId, dp.SystemId );
 
                //for the nodes with value but no children 
                case XmlNodeType.Text:
                    return this.CreateTextNode( dp.Value ); 
                case XmlNodeType.CDATA:
                    return this.CreateCDataSection( dp.Value );
                case XmlNodeType.ProcessingInstruction:
                    return this.CreateProcessingInstruction( dp.Name, dp.Value ); 
                case XmlNodeType.Comment:
                    return this.CreateComment( dp.Value ); 
                case XmlNodeType.Whitespace: 
                    return this.CreateWhitespace( dp.Value );
                case XmlNodeType.SignificantWhitespace: 
                    return this.CreateSignificantWhitespace( dp.Value );
                //for the nodes that don't have values, but might have children -- only clone the node and leave the children untouched
                case XmlNodeType.Element:
                    return this.CreateElement(dp.Prefix, dp.LocalName, dp.NamespaceURI); 
                case XmlNodeType.Attribute:
                    return this.CreateAttribute(dp.Prefix, dp.LocalName, dp.NamespaceURI); 
                case XmlNodeType.EntityReference: 
                    return this.CreateEntityReference( dp.Name );
            } 
            throw new InvalidOperationException( Res.GetString(Res.DataDom_CloneNode, dp.NodeType.ToString() ) );
        }

        internal static bool IsTextLikeNode( XmlNode n ) { 
            switch ( n.NodeType ) {
                case XmlNodeType.Text: 
                case XmlNodeType.CDATA: 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace: 
                    return true;

                case XmlNodeType.EntityReference:
                    Debug.Assert( false ); 
                    return false;
 
                default: 
                    return false;
            } 
        }

        internal bool IsNotMapped( DataColumn c ) {
            return DataSetMapper.IsNotMapped( c ); 
        }
 
        private bool IsSame( DataColumn c, int recNo1, int recNo2 ) { 
            if ( c.Compare( recNo1, recNo2 ) == 0 )
                return true; 

            return false;
        }
 
        internal bool IsTextOnly( DataColumn c ) {
            return c.ColumnMapping == MappingType.SimpleContent; 
        } 

 
        /// 
        ///    Loads the XML document from the specified file.
        /// 
        public override void Load( string filename ) { 
            this.bForceExpandEntity = true;
            base.Load( filename ); 
            this.bForceExpandEntity = false; 
        }
 
        /// 
        ///    Loads the XML document from the specified Stream.
        /// 
        public override void Load( Stream inStream ) { 
            this.bForceExpandEntity = true;
            base.Load( inStream ); 
            this.bForceExpandEntity = false; 
        }
 
        /// 
        ///    Loads the XML document from the specified TextReader.
        /// 
        public override void Load( TextReader txtReader ) { 
            this.bForceExpandEntity = true;
            base.Load( txtReader ); 
            this.bForceExpandEntity = false; 
        }
 
        /// 
        ///    Loads the XML document from the specified XmlReader.
        /// 
        public override void Load( XmlReader reader ) { 
            if ( this.FirstChild != null )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_MultipleLoad ) ); 
 
            try {
                ignoreXmlEvents = true; 

                // Unhook the DataRowCreatedSpecial listener, since we no longer base on the first created DataRow to do the Bind
                if ( fDataRowCreatedSpecial )
                    UnBindSpecialListeners(); 

                // We should NOT create DataRow objects when calling XmlDataDocument.CreateElement 
                fAssociateDataRow = false; 
                // Foliation s/b disabled
                isFoliationEnabled = false; 

                //now if we load from file we need to set the ExpandEntity flag to ExpandEntities
                if ( this.bForceExpandEntity ) {
                    Debug.Assert( reader is XmlTextReader ); 
                    ((XmlTextReader)reader).EntityHandling = EntityHandling.ExpandEntities;
                } 
                base.Load( reader ); 
                BindForLoad();
            } 
            finally {
                ignoreXmlEvents = false;
                isFoliationEnabled = true;
                autoFoliationState = ElementState.StrongFoliation; 
                fAssociateDataRow = true;
            } 
        } 

        private void LoadDataSetFromTree() { 
            ignoreDataSetEvents = true;
            ignoreXmlEvents = true;
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false; 
            bool saveEnforce = dataSet.EnforceConstraints;
            dataSet.EnforceConstraints = false; 
 
            try {
                Debug.Assert( DocumentElement != null ); 
                LoadRows( null, DocumentElement );
                SyncRows( null, DocumentElement, true );

                dataSet.EnforceConstraints = saveEnforce; 
            }
            finally { 
                ignoreDataSetEvents = false; 
                ignoreXmlEvents = false;
                IsFoliationEnabled = wasFoliationEnabled; 
            }
        }

        private void LoadTreeFromDataSet( DataSet ds ) { 
            ignoreDataSetEvents = true;
            ignoreXmlEvents = true; 
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled = false;
            this.fAssociateDataRow = false; 

            DataTable [] orderedTables = OrderTables(ds); // this  is to fix WebData 103397
            // problem is after we add support for Namespace  for DataTable, when infering we do not guarantee that table would be
            // in the same sequence that they were in XML because of namespace, some would be on different schema, so since they 
            // wont be in the same sequence as in XML, we may end up with having a child table, before its parent (which is not doable
            // with XML; and this happend because they are in different namespace) 
            // this kind of problems are known and please see comment in "OnNestedParentChange" 
            // 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
 
            try {
                for(int i = 0; i < orderedTables.Length; i++) {
                    DataTable t = orderedTables[i];
                    foreach( DataRow r in t.Rows ) { 
                        Debug.Assert( r.Element == null );
                        XmlBoundElement rowElem = AttachBoundElementToDataRow( r ); 
                        // 
                        switch ( r.RowState ) {
                        case DataRowState.Added: 
                        case DataRowState.Unchanged:
                        case DataRowState.Modified:
                            //
                            OnAddRow( r ); 
                            break;
                        case DataRowState.Deleted: 
                            // Nothing to do (the row already has an associated element as a fragment 
                            break;
                        case DataRowState.Detached: 
                            // We should not get rows in this state
                            Debug.Assert( false );
                            break;
                        default: 
                            // Unknown row state
                            Debug.Assert( false ); 
                            break; 
                        }
                    } 
                }
            }
            finally {
                ignoreDataSetEvents = false; 
                ignoreXmlEvents = false;
                IsFoliationEnabled = wasFoliationEnabled; 
                this.fAssociateDataRow = true; 
            }
        } 

        // load all data from tree structre into datarows
        private void LoadRows( XmlBoundElement rowElem, XmlNode node ) {
            Debug.Assert( node != null ); 

            XmlBoundElement be = node as XmlBoundElement; 
            if ( be != null ) { 
                DataTable dt = mapper.SearchMatchingTableSchema( rowElem, be );
 
                if ( dt != null ) {
                    DataRow r = GetRowFromElement( be );
                    Debug.Assert( r == null );
                    // If the rowElement was just created and has an un-initialized 
                    if ( be.ElementState == ElementState.None )
                        be.ElementState = ElementState.WeakFoliation; 
                    r = dt.CreateEmptyRow(); 
                    Bind( r, be );
 
                    // the region rowElem is now be
                    Debug.Assert( be.Row != null );
                    rowElem = be;
                } 
            }
            // recurse down for children 
            for ( XmlNode child = node.FirstChild; child != null; child = child.NextSibling ) 
                LoadRows( rowElem, child );
        } 

        internal DataSetMapper Mapper {
            get {
                return mapper; 
            }
        } 
 
        internal void OnDataRowCreated( object oDataSet, DataRow row ) {
            Debug.Assert( row.RowState == DataRowState.Detached ); 
            OnNewRow( row );
        }

        internal void OnClearCalled( object oDataSet, DataTable table ) { 
            throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_Clear ) );
        } 
 
        internal void OnDataRowCreatedSpecial( object oDataSet, DataRow row ) {
            Debug.Assert( row.RowState == DataRowState.Detached ); 

            // Register the regular events and un-register this one
            Bind(true);
            // Pass the event to the regular listener 
            OnNewRow( row );
        } 
        // Called when a new DataRow is created 
        internal void OnNewRow( DataRow row ) {
            Debug.Assert( row.Element == null ); 
            // Allow New state also because we are calling this function from
            Debug.Assert( row.RowState == DataRowState.Detached );

            AttachBoundElementToDataRow( row ); 
        }
 
        private XmlBoundElement AttachBoundElementToDataRow( DataRow row ) { 
            Debug.Assert( row.Element == null );
            DataTable table = row.Table; 
            // We shoould NOT call CreateElement here, since CreateElement will create and attach a new DataRow to the element
            XmlBoundElement rowElement = new XmlBoundElement( string.Empty, table.EncodedTableName, table.Namespace, this );
            rowElement.IsEmpty = false;
            Bind( row, rowElement ); 
            rowElement.ElementState = ElementState.Defoliated;
            return rowElement; 
        } 

        private bool NeedXSI_NilAttr( DataRow row ) { 
            DataTable tb = row.Table;
            Debug.Assert( tb != null ) ;
            if ( tb.xmlText == null )
                return false; 
            object value = row[tb.xmlText];
            return ( Convert.IsDBNull( value ) ); 
        } 

        private void OnAddRow( DataRow row ) { 
            // Xml operations in this func should not trigger ROM operations
            Debug.Assert( this.ignoreXmlEvents == true );

            XmlBoundElement rowElement = (XmlBoundElement)(GetElementFromRow( row )); 
            Debug.Assert( rowElement != null );
 
            if ( NeedXSI_NilAttr( row ) && !rowElement.IsFoliated ) 
                //we need to foliate it because we need to add one more attribute xsi:nil = true;
                ForceFoliation( rowElement, AutoFoliationState ); 

            Debug.Assert( rowElement != null );
            DataRow rowDocElem = GetRowFromElement( this.DocumentElement );
            if ( rowDocElem != null ) { 
                DataRow parentRow = GetNestedParent( row );
                if ( parentRow == null ) 
                    DemoteDocumentElement(); 
            }
            EnsureDocumentElement().AppendChild( rowElement ); 

            // Move the children of the row under
            FixNestedChildren( row, rowElement );
            OnNestedParentChange( row, rowElement, null ); 
        }
 
        private void OnColumnValueChanged( DataRow row, DataColumn col, XmlBoundElement rowElement ) { 
            //
            if ( IsNotMapped(col) ) 
                goto lblDoNestedRelationSync;

            object value = row[col];
 
            if ( col.ColumnMapping == MappingType.SimpleContent && Convert.IsDBNull( value ) && !rowElement.IsFoliated )
                ForceFoliation( rowElement, ElementState.WeakFoliation); 
            else { 
                // no need to [....] if not foliated
                if ( !IsFoliated( rowElement ) ) { 
#if DEBUG
                    // If the new value is null, we should be already foliated if there is a DataPointer that points to the column
                    // (see OnRowChanging, case DataRowAction.Change)
                    if ( Convert.IsDBNull( row[col, DataRowVersion.Current] ) ) { 
                        try {
                            if ( pointers.Count > 0 ) { 
                                object pointer = null; 
                                foreach( DictionaryEntry entry in pointers ) {
                                    pointer = entry.Value; 
                                    Debug.Assert( (pointer != null) && ! ((IXmlDataVirtualNode)pointer).IsOnColumn( col ) );
                                }
                            }
                        } 
                        catch (Exception e) {
                            // 
                            if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) { 
                                throw;
                            } 
                            // 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
                        }
                    }
#endif 
                    goto lblDoNestedRelationSync;
                } 
            } 

            if ( IsTextOnly( col ) ) { 
                if ( Convert.IsDBNull( value ) ) {
                    value = String.Empty;
                    //make sure that rowElement has Attribute xsi:nil and its value is true
                    XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL); 
                    if ( attr == null ) {
                        attr = CreateAttribute( XSI, Keywords.XSI_NIL, Keywords.XSINS ); 
                        attr.Value = Keywords.TRUE; 
                        rowElement.SetAttributeNode( attr );
                        this.bHasXSINIL = true; 
                    }
                    else
                        attr.Value = Keywords.TRUE;
                } 
                else {
                    //make sure that if rowElement has Attribute xsi:nil, its value is false 
                    XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL); 
                    if ( attr != null )
                        attr.Value = Keywords.FALSE; 
                }
                ReplaceInitialChildText( rowElement, col.ConvertObjectToXml( value ) );
                goto lblDoNestedRelationSync;
            } 

            // update the attribute that maps to the column 
            bool fFound = false; 

            // Find the field node and set it's value 
            if (col.ColumnMapping == MappingType.Attribute) {
                foreach( XmlAttribute attr in rowElement.Attributes ) {
                    if ( attr.LocalName == col.EncodedColumnName && attr.NamespaceURI == col.Namespace ) {
                        if ( Convert.IsDBNull( value ) ) { 
                            attr.OwnerElement.Attributes.Remove( attr );
                        } 
                        else { 
                            attr.Value = col.ConvertObjectToXml( value );
                        } 
                        fFound = true;
                        break;
                    }
                } 

                // create new attribute if we didn't find one. 
                if ( !fFound && ! Convert.IsDBNull( value ) ) { 
                    rowElement.SetAttribute( col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml( value ) );
                } 
            }
            else {
                // update elements that map to the column...
                RegionIterator iter = new RegionIterator( (XmlBoundElement)rowElement ); 
                bool fMore = iter.Next();
                while ( fMore ) { 
                    if ( iter.CurrentNode.NodeType == XmlNodeType.Element ) { 
                        XmlElement e = (XmlElement) iter.CurrentNode;
                        Debug.Assert( e != null ); 
                        //we should skip the subregion
                        XmlBoundElement be = e as XmlBoundElement;
                        if ( be != null && be.Row != null ) {
                            fMore = iter.NextRight(); //skip over the sub-region 
                            continue;
                        } 
                        if ( e.LocalName == col.EncodedColumnName && e.NamespaceURI == col.Namespace ) { 
                            fFound = true;
                            if ( Convert.IsDBNull( value ) ) { 
                                PromoteNonValueChildren( e );
                                fMore = iter.NextRight();
                                e.ParentNode.RemoveChild( e );
                                // keep looking for more matching elements 
                                continue;
                            } 
                            else { 
                                ReplaceInitialChildText( e, col.ConvertObjectToXml( value ) );
                                //make sure that if the Element has Attribute xsi:nil, its value is false 
                                XmlAttribute attr = e.GetAttributeNode(XSI_NIL);
                                if ( attr != null )
                                    attr.Value = Keywords.FALSE;
                                // no need to look any further. 
                                goto lblDoNestedRelationSync;
                            } 
                        } 
                    }
                    fMore = iter.Next(); 
                }

                // create new element if we didn't find one.
                if ( !fFound && ! Convert.IsDBNull( value ) ) { 
                    XmlElement newElem = new XmlBoundElement( string.Empty, col.EncodedColumnName, col.Namespace, this );
                    newElem.AppendChild( CreateTextNode( col.ConvertObjectToXml( value ) ) ); 
 
                    XmlNode elemBefore = GetColumnInsertAfterLocation( row, col, rowElement );
                    if ( elemBefore != null ) { 
                        rowElement.InsertAfter( newElem, elemBefore );
                    }
                    else if ( rowElement.FirstChild != null ) {
                        rowElement.InsertBefore( newElem, rowElement.FirstChild ); 
                    }
                    else { 
                        rowElement.AppendChild( newElem ); 
                    }
                } 
            }
lblDoNestedRelationSync:
            // Change the XML to conform to the (potentially) change in parent nested relation
            DataRelation relation = GetNestedParentRelation(row); 
            if ( relation != null ) {
                Debug.Assert( relation.ChildTable == row.Table ); 
                if ( relation.ChildKey.ContainsColumn( col ) ) 
                    OnNestedParentChange( row, rowElement, col );
            } 
        }

        private void OnColumnChanged( object sender, DataColumnChangeEventArgs args ) {
            // You should not be able to make DataRow field changes if the DataRow is deleted 
            Debug.Assert( args.Row.RowState != DataRowState.Deleted );
 
            if ( ignoreDataSetEvents ) 
                return;
 
            bool wasIgnoreXmlEvents = ignoreXmlEvents;
            ignoreXmlEvents = true;
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false; 

            try { 
                DataRow row     = args.Row; 
                DataColumn col  = args.Column;
                object oVal     = args.ProposedValue; 

                if ( row.RowState == DataRowState.Detached ) {
                    XmlBoundElement be = row.Element;
                    Debug.Assert( be != null ); 
                    if ( be.IsFoliated ) {
                        // Need to [....] changes from ROM to DOM 
                        OnColumnValueChanged( row, col, be ); 
                    }
                } 
            }
            finally {
                IsFoliationEnabled = wasFoliationEnabled;
                ignoreXmlEvents = wasIgnoreXmlEvents; 
            }
        } 
 
        private void OnColumnValuesChanged( DataRow row, XmlBoundElement rowElement ) {
            Debug.Assert( row != null ); 
            Debug.Assert( rowElement != null );

            // If user has cascading relationships, then columnChangeList will contains the changed columns only for the last row beeing cascaded
            // but there will be multiple ROM events 
            if ( columnChangeList.Count > 0 ) {
                if ( ((DataColumn)(columnChangeList[0])).Table == row.Table ) { 
                    foreach( DataColumn c in columnChangeList ) 
                        OnColumnValueChanged( row, c, rowElement );
                } 
                else {
                    foreach( DataColumn c in row.Table.Columns )
                        OnColumnValueChanged( row, c, rowElement );
                } 
            }
            else { 
                foreach( DataColumn c in row.Table.Columns ) 
                    OnColumnValueChanged( row, c, rowElement );
            } 
            columnChangeList.Clear();
        }

        private void OnDeleteRow( DataRow row, XmlBoundElement rowElement ) { 
            // IgnoreXmlEvents s/b on since we are manipulating the XML tree and we not want this to reflect in ROM view.
            Debug.Assert( this.ignoreXmlEvents == true ); 
            // Special case when rowElem is document element: we create a new docElem, move the current one as a child of 
            // the new created docElem, then process as if the docElem is not a rowElem
            if ( rowElement == this.DocumentElement ) 
                DemoteDocumentElement();

            PromoteInnerRegions( rowElement );
            rowElement.ParentNode.RemoveChild( rowElement ); 
        }
 
        private void OnDeletingRow( DataRow row, XmlBoundElement rowElement ) { 
            // Note that this function is beeing called even if ignoreDataSetEvents == true.
 
            // Foliate, so we can be able to preserve the nodes even if the DataRow has no longer values for the crtRecord.
            if ( IsFoliated( rowElement ) )
                return;
 
            bool wasIgnoreXmlEvents  = IgnoreXmlEvents;
            IgnoreXmlEvents          = true; 
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled       = true;
            try { 
                Foliate( rowElement );
            }
            finally {
                IsFoliationEnabled = wasFoliationEnabled; 
                IgnoreXmlEvents    = wasIgnoreXmlEvents;
            } 
        } 

        private void OnFoliated( XmlNode node ) { 
            while ( true ) {
                try {
                    if ( pointers.Count > 0 ) {
                        foreach( DictionaryEntry entry in pointers ) { 
                            object pointer = entry.Value;
                            Debug.Assert( pointer != null ); 
                            ((IXmlDataVirtualNode)pointer).OnFoliated( node ); 
                        }
                    } 
                    return;
                }
                catch (Exception e) {
                    // 
                    if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) {
                        throw; 
                    } 
                    // This can happens only when some threads are creating navigators (thus modifying this.pointers) while other threads are in the foreach loop.
                    // Solution is to re-try OnFoliated. 
                }
            }
            // You should never get here in regular cases
        } 

        DataColumn FindAssociatedParentColumn( DataRelation relation, DataColumn childCol ) { 
            DataColumn[] columns = relation.ChildKey.ColumnsReference; 
            for (int i = 0; i < columns.Length; i++) {
                if ( childCol == columns[i] ) 
                    return relation.ParentKey.ColumnsReference[i];
            }
            return null;
        } 

        // Change the childElement position in the tree to conform to the parent nested relationship in ROM 
        private void OnNestedParentChange(DataRow child, XmlBoundElement childElement, DataColumn childCol) { 
            Debug.Assert( child.Element == childElement && childElement.Row == child );
            // This function is (and s/b) called as a result of ROM changes, therefore XML changes done here should not be [....]-ed to ROM 
            Debug.Assert( ignoreXmlEvents == true );
#if DEBUG
            // In order to check that this move does not change the connected/disconnected state of the node
            bool fChildElementConnected = IsConnected( childElement ); 
#endif
            DataRow parentRowInTree; 
            if ( childElement == this.DocumentElement || childElement.ParentNode == null ) 
                parentRowInTree = null;
            else 
                parentRowInTree = GetRowFromElement( (XmlElement) childElement.ParentNode );
            DataRow parentRowInRelation = GetNestedParent(child);

            if ( parentRowInTree != parentRowInRelation ) { 
                if ( parentRowInRelation != null ) {
                    // 
 
                    XmlElement newParent = GetElementFromRow( parentRowInRelation );
                    newParent.AppendChild( childElement ); 
                }
                else {
                    // no parent? Maybe the parentRow is during changing or childCol is the ID is set to null ( detached from the parent row ).
                    DataRelation relation = GetNestedParentRelation(child); 
                    if ( childCol == null || relation == null || Convert.IsDBNull(child[childCol]) ) {
                        EnsureNonRowDocumentElement().AppendChild( childElement ); 
                    } 
                    else {
                        DataColumn colInParent = FindAssociatedParentColumn( relation, childCol ); 
                        Debug.Assert( colInParent != null );
                        object comparedValue = colInParent.ConvertValue(child[childCol]);
                        if (parentRowInTree.tempRecord != -1 && colInParent.CompareValueTo( parentRowInTree.tempRecord, comparedValue ) != 0 ) {
                            EnsureNonRowDocumentElement().AppendChild( childElement ); 
                        }
                        //else do nothing because its original parentRowInRelation will be changed so that this row will still be its child 
                    } 
                }
            } 
#if DEBUG
            // 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
            // 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)
            // 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. 
            Debug.Assert( fChildElementConnected == IsConnected( childElement ) );
            Debug.Assert( IsRowLive( child ) ? IsConnected( childElement ) : ! IsConnected( childElement ) ); 
#endif 
        }
 
        private void OnNodeChanged( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents )
                return;
 
            bool wasIgnoreDataSetEvents = ignoreDataSetEvents;
            bool wasIgnoreXmlEvents     = ignoreXmlEvents; 
            bool wasFoliationEnabled    = IsFoliationEnabled; 
            ignoreDataSetEvents = true;
            ignoreXmlEvents     = true; 
            IsFoliationEnabled  = false;
            bool fEnableCascading = DataSet.fEnableCascading;
            DataSet.fEnableCascading = false;
 
            try {
                // okay to allow text node value changes when bound. 
                XmlBoundElement rowElement = null; 

                Debug.Assert( DataSet.EnforceConstraints == false ); 

                if ( mapper.GetRegion( args.Node, out rowElement ) ) {
                    SynchronizeRowFromRowElement( rowElement );
                } 
            }
            finally { 
                ignoreDataSetEvents = wasIgnoreDataSetEvents; 
                ignoreXmlEvents     = wasIgnoreXmlEvents;
                IsFoliationEnabled  = wasFoliationEnabled; 
                DataSet.fEnableCascading = fEnableCascading;
            }
        }
 
        private void OnNodeChanging( object sender, XmlNodeChangedEventArgs args ) {
            if( ignoreXmlEvents ) 
                return; 
            if ( DataSet.EnforceConstraints != false )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_EnforceConstraintsShouldBeOff ) ); 
        }


        private void OnNodeInserted( object sender, XmlNodeChangedEventArgs args ) { 
            if ( ignoreXmlEvents )
                return; 
 
            bool wasIgnoreDataSetEvents = ignoreDataSetEvents;
            bool wasIgnoreXmlEvents     = ignoreXmlEvents; 
            bool wasFoliationEnabled    = IsFoliationEnabled;
            ignoreDataSetEvents = true;
            ignoreXmlEvents     = true;
            IsFoliationEnabled  = false; 

            Debug.Assert( DataSet.EnforceConstraints == false ); 
 
            bool fEnableCascading = DataSet.fEnableCascading;
            DataSet.fEnableCascading = false; 

            try {
                // Handle both new node inserted and 2nd part of a move operation.
                // 
                XmlNode node = args.Node;
                XmlNode oldParent = args.OldParent; 
                XmlNode newParent = args.NewParent; 

                // 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. 
                Debug.Assert( oldParent == null );
                if ( IsConnected( newParent ) ) {
                    // Inserting a node to connected tree
                    OnNodeInsertedInTree( node ); 
                }
                else { 
                    // Inserting a node to disconnected tree 
                    OnNodeInsertedInFragment( node );
                } 
            }
            finally {
                ignoreDataSetEvents = wasIgnoreDataSetEvents;
                ignoreXmlEvents     = wasIgnoreXmlEvents; 
                IsFoliationEnabled  = wasFoliationEnabled;
                DataSet.fEnableCascading = fEnableCascading; 
            } 

        } 

        private void OnNodeInserting( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents )
                return; 
            if ( DataSet.EnforceConstraints != false )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_EnforceConstraintsShouldBeOff ) ); 
        } 

 
        private void OnNodeRemoved( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents )
                return;
 
            bool wasIgnoreDataSetEvents = ignoreDataSetEvents;
            bool wasIgnoreXmlEvents     = ignoreXmlEvents; 
            bool wasFoliationEnabled    = IsFoliationEnabled; 
            ignoreDataSetEvents = true;
            ignoreXmlEvents     = true; 
            IsFoliationEnabled  = false;

            Debug.Assert( DataSet.EnforceConstraints == false );
 
            bool fEnableCascading = DataSet.fEnableCascading;
            DataSet.fEnableCascading = false; 
 
            try {
                XmlNode node = args.Node; 
                XmlNode oldParent = args.OldParent;
                Debug.Assert( args.NewParent == null );

                if ( IsConnected( oldParent ) ) { 
                    // Removing from connected tree to disconnected tree
                    OnNodeRemovedFromTree( node, oldParent ); 
                } 
                else {
                    // Removing from disconnected tree to disconnected tree: just [....] the old region 
                    OnNodeRemovedFromFragment( node, oldParent );
                }
            }
            finally { 
                ignoreDataSetEvents = wasIgnoreDataSetEvents;
                ignoreXmlEvents     = wasIgnoreXmlEvents; 
                IsFoliationEnabled  = wasFoliationEnabled; 
                DataSet.fEnableCascading = fEnableCascading;
            } 
        }

        private void OnNodeRemoving( object sender, XmlNodeChangedEventArgs args ) {
            if ( ignoreXmlEvents ) 
                return;
            if ( DataSet.EnforceConstraints != false ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_EnforceConstraintsShouldBeOff ) ); 
        }
 
        // Node was removed from connected tree to disconnected tree
        private void OnNodeRemovedFromTree( XmlNode node, XmlNode oldParent ) {
            XmlBoundElement oldRowElem;
 
            // Synchronize values from old region
            if ( mapper.GetRegion( oldParent, out oldRowElem ) ) 
                SynchronizeRowFromRowElement( oldRowElem ); 

            // Disconnect all regions, starting w/ node (if it is a row-elem) 
            XmlBoundElement rowElem = node as XmlBoundElement;
            if ( rowElem != null && rowElem.Row != null )
                EnsureDisconnectedDataRow( rowElem );
            TreeIterator iter = new TreeIterator( node ); 
            for ( bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement() ) {
                rowElem = (XmlBoundElement)(iter.CurrentNode); 
                EnsureDisconnectedDataRow( rowElem ); 
            }
 
            // Assert that all sub-regions are disconnected
            AssertNonLiveRows( node );
        }
        // Node was removed from the disconnected tree to disconnected tree 
        private void OnNodeRemovedFromFragment( XmlNode node, XmlNode oldParent ) {
            XmlBoundElement oldRowElem; 
 
            if ( mapper.GetRegion( oldParent, out oldRowElem ) ) {
                // [....] the old region if it is not deleted 
                DataRow row = oldRowElem.Row;
                // Since the old old region was disconnected, then the row can be only Deleted or Detached
                Debug.Assert( ! IsRowLive( row ) );
                if ( oldRowElem.Row.RowState == DataRowState.Detached ) 
                    SynchronizeRowFromRowElement( oldRowElem );
            } 
 
            // Need to set nested for the sub-regions (if node is a row-elem, we need to set it just for itself)
            XmlBoundElement be = node as XmlBoundElement; 
            if ( be != null && be.Row != null ) {
                Debug.Assert( ! IsRowLive( be.Row ) );
                SetNestedParentRegion( be, null );
            } 
            else {
                // Set nested parent to null for all child regions 
                TreeIterator iter = new TreeIterator( node ); 
                for ( bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() ) {
                    XmlBoundElement rowElemChild = (XmlBoundElement)(iter.CurrentNode); 
                    SetNestedParentRegion( rowElemChild, null );
                }
            }
 
            // Assert that all sub-regions are disconnected
            AssertNonLiveRows( node ); 
        } 

 
        private void OnRowChanged( object sender, DataRowChangeEventArgs args ) {
            if ( ignoreDataSetEvents )
                return;
 
            ignoreXmlEvents = true;
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled = false; 

            try { 
                DataRow row = args.Row;
                XmlBoundElement rowElement = row.Element;
                // We should have an associated row-elem created when the DataRow was created (or at the load time)
                Debug.Assert( rowElement != null ); 

                switch ( args.Action ) { 
                    case DataRowAction.Add: 
                        //
 



 

 
 
                        OnAddRow( row );
                        break; 

                    case DataRowAction.Delete:
                        OnDeleteRow( row, rowElement );
                        break; 

                    case DataRowAction.Rollback: 
                        switch ( rollbackState ) { 
                            case DataRowState.Deleted:
                                OnUndeleteRow( row, rowElement ); 
                                UpdateAllColumns( row, rowElement );
                                break;

                            case DataRowState.Added: 
                                rowElement.ParentNode.RemoveChild( rowElement );
                                break; 
 
                            case DataRowState.Modified:
                                OnColumnValuesChanged( row, rowElement ); 
                                break;
                        }
                        break;
 
                    case DataRowAction.Change:
                        OnColumnValuesChanged( row, rowElement ); 
                        break; 

                    case DataRowAction.Commit: 
                        if ( row.RowState == DataRowState.Detached ) {
                            //by now, all the descendent of the element that is not of this region should have been promoted already
                            rowElement.RemoveAll();
                        } 
                        break;
                    default: 
                        //Console.WriteLine("Other Event"); 
                        break;
                } 
            }
            finally {
                IsFoliationEnabled = wasFoliationEnabled;
                ignoreXmlEvents = false; 
            }
        } 
 
        private void OnRowChanging( object sender, DataRowChangeEventArgs args ) {
            // We foliate the region each time the assocaited row gets deleted 
            DataRow row = args.Row;
            if ( args.Action == DataRowAction.Delete && row.Element != null ) {
                OnDeletingRow( row, row.Element );
                return; 
            }
 
            if ( ignoreDataSetEvents ) 
                return;
 
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false;

            try { 
                ignoreXmlEvents = true;
 
                XmlElement rowElement = GetElementFromRow( row ); 

                int nRec1 = -1; 
                int nRec2 = -1;

                if ( rowElement != null ) {
                    switch ( args.Action ) { 
                        case DataRowAction.Add:
                            // DataRow is beeing added to the table (Table.Rows.Add is beeing called) 
                            break; 

                        case DataRowAction.Delete: 
                            // DataRow is beeing deleted
                            //    - state transition from New (AKA PendingInsert) to Detached (AKA Created)
                            //    - state transition from Unchanged to Deleted (AKA PendingDelete)
                            //    - state transition from Modified (AKA PendingChange) to Delete (AKA PendingDelete) 
                            Debug.Assert( false );  // This should have been handled above, irrespective of ignoreDataSetEvents value (true or false)
                            break; 
 
                        case DataRowAction.Rollback:
                            // DataRow gets reverted to previous values (by calling DataRow.RejectChanges): 
                            //    - state transition from Detached (AKA Created) to Detached (AKA Created)
                            //    - state transition from New (AKA PendingInsert) to Detached (AKA Created)
                            //    - state transition from Modified (AKA PendingChange) to Unchanged
                            //    - state transition from Deleted (AKA PendingDelete) to Unchanged 
                            rollbackState = row.RowState;
                            switch ( rollbackState ) { 
                                case DataRowState.Deleted: 
                                    break;
 
                                case DataRowState.Detached:
                                    break;

                                case DataRowState.Added: 
                                    break;
 
                                case DataRowState.Modified: 
                                    columnChangeList.Clear();
                                    nRec1 = row.GetRecordFromVersion(DataRowVersion.Original); 
                                    nRec2 = row.GetRecordFromVersion(DataRowVersion.Current);
                                    foreach( DataColumn c in row.Table.Columns ) {
                                        if ( !IsSame( c, nRec1, nRec2 ) )
                                            columnChangeList.Add(c); 
                                    }
                                    break; 
 
                            }
                            break; 

                        case DataRowAction.Change:
                            // A DataRow field is beeing changed
                            //    - state transition from New (AKA PendingInsert) to New (AKA PendingInsert) 
                            //    - state transition from Unchanged to Modified (AKA PendingChange)
                            //    - state transition from Modified (AKA PendingChange) to Modified (AKA PendingChange) 
                            // 
                            columnChangeList.Clear();
                            nRec1 = row.GetRecordFromVersion( DataRowVersion.Proposed ); 
                            nRec2 = row.GetRecordFromVersion( DataRowVersion.Current );
                            foreach( DataColumn c in row.Table.Columns ) {
                                object proposedValue = row[c, DataRowVersion.Proposed];
                                object currentValue  = row[c, DataRowVersion.Current]; 
                                // Foliate if proposedValue is DBNull; this way the DataPointer objects will point to a disconnected fragment after
                                // the DBNull value is beeing set 
                                if ( Convert.IsDBNull( proposedValue ) && ! Convert.IsDBNull( currentValue ) ) { 
                                    // Foliate only for non-hidden columns (since hidden cols are not represented in XML)
                                    if ( c.ColumnMapping != MappingType.Hidden ) 
                                        FoliateIfDataPointers( row, rowElement );
                                }
                                if ( !IsSame( c, nRec1, nRec2 ) )
                                    columnChangeList.Add(c); 
                            }
                            break; 
 
                        case DataRowAction.Commit:
                            break; 
                    }
                }
            }
            finally { 
                ignoreXmlEvents = false;
                IsFoliationEnabled = wasFoliationEnabled; 
            } 
        }
 
        private void OnDataSetPropertyChanging( object oDataSet, PropertyChangedEventArgs args ) {
            if ( args.PropertyName == "DataSetName" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_DataSetNameChange ) );
            // 
        }
        private void OnColumnPropertyChanging( object oColumn, PropertyChangedEventArgs args ) { 
            if ( args.PropertyName == "ColumnName" ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_ColumnNameChange ) );
            if ( args.PropertyName == "Namespace" ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_ColumnNamespaceChange ) );
            if ( args.PropertyName == "ColumnMapping" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_ColumnMappingChange ) );
        } 
        private void OnTablePropertyChanging( object oTable, PropertyChangedEventArgs args ) {
            if ( args.PropertyName == "TableName" ) 
                throw new InvalidOperationException( Res.GetString(Res.DataDom_TableNameChange ) ); 
            if ( args.PropertyName == "Namespace" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_TableNamespaceChange ) ); 
        }
        private void OnTableColumnsChanging( object oColumnsCollection, CollectionChangeEventArgs args ) {
            // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
            // args.Element is one of either the column (for Add and Remove actions or null, if the entire colection of columns is changing) 

            // Disallow changing the columns collection (since we are subscribed only in populated mode, we allow changes in any state but non-populated mode) 
            throw new InvalidOperationException(  Res.GetString(Res.DataDom_TableColumnsChange ) ); 
        }
 
        private void OnDataSetTablesChanging( object oTablesCollection, CollectionChangeEventArgs args ) {
            // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
            // args.Element is a table (dont know if it can be null:
 
            // Disallow changing the tables collection (since we are subscribed only in populated mode, we allow changes in any state but non-populated mode)
            throw new InvalidOperationException(  Res.GetString(Res.DataDom_DataSetTablesChange ) ); 
        } 

        private void OnDataSetRelationsChanging( object oRelationsCollection, CollectionChangeEventArgs args ) { 
            // args.Action is one of CollectionChangeAction.Add, CollectionChangeAction.Remove or CollectionChangeAction.Refresh
            // args.Element is a DataRelation (dont know if it can be null:

            // Disallow changing the tables collection if there is data loaded and there are nested relationship that are added/refreshed 
            DataRelation rel = (DataRelation)(args.Element);
            if ( rel != null && rel.Nested ) 
                throw new InvalidOperationException(  Res.GetString(Res.DataDom_DataSetNestedRelationsChange ) ); 

            // If Add and Remove, we should already been throwing if .Nested == false 
            Debug.Assert( ! (args.Action == CollectionChangeAction.Add || args.Action == CollectionChangeAction.Remove) || rel.Nested == false );
            if ( args.Action == CollectionChangeAction.Refresh ) {
                foreach ( DataRelation relTemp in (DataRelationCollection)oRelationsCollection ) {
                    if ( relTemp.Nested ) { 
                        throw new InvalidOperationException(  Res.GetString(Res.DataDom_DataSetNestedRelationsChange ) );
                    } 
                } 
            }
        } 

        private void OnRelationPropertyChanging( object oRelationsCollection, PropertyChangedEventArgs args ) {
            if ( args.PropertyName == "Nested" )
                throw new InvalidOperationException( Res.GetString(Res.DataDom_DataSetNestedRelationsChange ) ); 
        }
 
        private void OnUndeleteRow( DataRow row, XmlElement rowElement ) { 
            XmlNode refRow;
            XmlElement parent; 

            // make certain we weren't place somewhere else.
            if ( rowElement.ParentNode != null )
                rowElement.ParentNode.RemoveChild( rowElement ); 

            // Find the parent of RowNode to be inserted 
            DataRow parentRowInRelation = GetNestedParent(row); 
            if (parentRowInRelation == null) {
                parent = EnsureNonRowDocumentElement(); 
            }
            else
                parent = GetElementFromRow(parentRowInRelation);
 
            if ((refRow = GetRowInsertBeforeLocation(row, rowElement, parent)) != null)
                parent.InsertBefore(rowElement, refRow); 
            else 
                parent.AppendChild( rowElement );
 
            FixNestedChildren(row, rowElement);
        }

        // Promote the rowElemChild node/region after prevSibling node (as the next sibling) 
        private void PromoteChild( XmlNode child, XmlNode prevSibling ) {
            // It makes no sense to move rowElemChild on the same level 
            Debug.Assert( child.ParentNode != prevSibling.ParentNode ); 
            // prevSibling must have a parent, since we want to add a sibling to it
            Debug.Assert( prevSibling.ParentNode != null ); 
            Debug.Assert( IsFoliationEnabled == false );
            Debug.Assert( IgnoreXmlEvents == true );
            // Should not insert after docElem node
            Debug.Assert( prevSibling != this.DocumentElement ); 

            if ( child.ParentNode != null ) 
                child.ParentNode.RemoveChild( child ); 

            Debug.Assert( child.ParentNode == null ); 
            prevSibling.ParentNode.InsertAfter( child, prevSibling );
        }

        // Promote child regions under parent as next siblings of parent 
        private void PromoteInnerRegions( XmlNode parent ) {
            Debug.Assert( parent != null ); 
            Debug.Assert( parent.NodeType != XmlNodeType.Attribute );   // We need to get get the grand-parent region 
            Debug.Assert( parent != DocumentElement );                  // We cannot promote children of the DocumentElement
 
            XmlNode prevSibling = parent;
            XmlBoundElement parentRegionRowElem;
            mapper.GetRegion( parent.ParentNode, out parentRegionRowElem );
 
            TreeIterator iter = new TreeIterator( parent );
            bool fMore = iter.NextRowElement(); 
            while ( fMore ) { 
                Debug.Assert( iter.CurrentNode is XmlBoundElement && ((XmlBoundElement)(iter.CurrentNode)).Row != null );
                XmlBoundElement rowElemChild = (XmlBoundElement)(iter.CurrentNode); 
                fMore = iter.NextRightRowElement();
                PromoteChild( rowElemChild, prevSibling );
                SetNestedParentRegion( rowElemChild, parentRegionRowElem );
            } 
        }
 
        private void PromoteNonValueChildren( XmlNode parent ) { 
            Debug.Assert( parent != null );
            XmlNode prevSibling = parent; 
            XmlNode child = parent.FirstChild;
            bool bTextLikeNode = true;
            XmlNode nextSibling = null;
            while ( child != null ) { 
                nextSibling = child.NextSibling;
                if (!bTextLikeNode || !IsTextLikeNode(child)) { 
                    bTextLikeNode = false; 
                    nextSibling = child.NextSibling;
                    PromoteChild( child, prevSibling ); 
                    prevSibling = child;
                }
                child = nextSibling;
            } 
        }
 
        private void RemoveInitialTextNodes( XmlNode node ) { 
            while ( node != null && IsTextLikeNode( node ) ) {
                XmlNode sibling = node.NextSibling; 
                node.ParentNode.RemoveChild( node );
                node = sibling;
            }
        } 

        private void ReplaceInitialChildText( XmlNode parent, string value ) { 
            XmlNode n = parent.FirstChild; 

            // don't consider whitespace when replacing initial text 
            while ( n != null && n.NodeType == XmlNodeType.Whitespace )
                n = n.NextSibling;

            if ( n != null ) { 
                if ( n.NodeType == XmlNodeType.Text )
                    n.Value = value; 
                else 
                    n = parent.InsertBefore( CreateTextNode( value ), n );
                RemoveInitialTextNodes( n.NextSibling ); 
            }
            else {
                parent.AppendChild( CreateTextNode( value ) );
            } 
        }
 
        internal XmlNode SafeFirstChild( XmlNode n ) { 
            XmlBoundElement be = n as XmlBoundElement;
            if ( be != null ) 
                return be.SafeFirstChild;
            else
                //other type of node should be already foliated.
                return n.FirstChild; 
        }
 
        internal XmlNode SafeNextSibling( XmlNode n ) { 
            XmlBoundElement be = n as XmlBoundElement;
            if ( be != null ) 
                return be.SafeNextSibling;
            else
                //other type of node should be already foliated.
                return n.NextSibling; 
        }
 
        internal XmlNode SafePreviousSibling( XmlNode n ) { 
            XmlBoundElement be = n as XmlBoundElement;
            if ( be != null ) 
                return be.SafePreviousSibling;
            else
                //other type of node should be already foliated.
                return n.PreviousSibling; 
        }
 
        internal static void SetRowValueToNull( DataRow row, DataColumn col ) { 
            Debug.Assert( col.ColumnMapping != MappingType.Hidden );
            Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 

            //
            if ( ! ( row.IsNull( col ) ) )
                row[ col ] = Convert.DBNull; 
        }
 
        internal static void SetRowValueFromXmlText( DataRow row, DataColumn col, string xmlText ) { 
            Debug.Assert( xmlText != null );
            Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 
            object oVal;
            try {
                oVal = col.ConvertXmlToObject( xmlText );
                // This func does not set the field value to null - call SetRowValueToNull in order to do so 
                Debug.Assert( oVal != null && ! ( oVal is DBNull ) );
            } 
            catch (Exception e) { 
                //
                if (!System.Data.Common.ADP.IsCatchableExceptionType (e)) { 
                        throw;
                }
                // Catch data-type errors and set ROM to Unspecified value
                SetRowValueToNull( row, col ); 
                return;
            } 
 
            if ( ! oVal.Equals( row[col] ) )
                row[ col ] = oVal; 
        }

        private void SynchronizeRowFromRowElement( XmlBoundElement rowElement ) {
            SynchronizeRowFromRowElement( rowElement, null ); 
        }
        // [....] row fields w/ values from rowElem region. 
        // If rowElemList is != null, all subregions of rowElem are appended to it. 
        private void SynchronizeRowFromRowElement( XmlBoundElement rowElement, ArrayList rowElemList ) {
            DataRow row = rowElement.Row; 
            Debug.Assert( row != null );

            // No synchronization needed for deleted rows
            if ( row.RowState == DataRowState.Deleted ) 
                return;
 
            row.BeginEdit(); 
#if DEBUG
            try { 
#endif
                SynchronizeRowFromRowElementEx( rowElement, rowElemList );
#if DEBUG
            } 
            catch {
                // We should not get any exceptions because we always handle data-type conversion 
                Debug.Assert( false ); 
                throw;
            } 
#endif
#if DEBUG
            try {
#endif 
                row.EndEdit();
#if DEBUG 
            } 
            catch {
                // We should not get any exceptions because DataSet.EnforceConstraints should be always off 
                //
                Debug.Assert( false );
                throw;
            } 
#endif
        } 
        private void SynchronizeRowFromRowElementEx( XmlBoundElement rowElement, ArrayList rowElemList ) { 
            Debug.Assert( rowElement != null );
            Debug.Assert( rowElement.Row != null ); 
            Debug.Assert( this.DataSet.EnforceConstraints == false );

            DataRow row = rowElement.Row;
            Debug.Assert( row != null ); 
            DataTable table = row.Table;
 
            // if not foliated, already synch'd 
//            if ( !IsFoliated(rowElement) )
//                return; 
            //Debug.Assert( IsFoliated(rowElement) ); // If foliated we should not get the event (should be handled directly by DataPointer)

            Hashtable foundColumns = new Hashtable();
            string xsi_attrVal = string.Empty; 

            RegionIterator iter = new RegionIterator( rowElement ); 
            bool fMore; 
            // If present, fill up the TextOnly column
            DataColumn column = GetTextOnlyColumn( row ); 
            if ( column != null ) {
                foundColumns[column] = column;
                string value;
                fMore = iter.NextInitialTextLikeNodes( out value ); 
                if ( value.Length == 0 && ( ( (xsi_attrVal = rowElement.GetAttribute(XSI_NIL) ) == "1" ) || xsi_attrVal == "true" ) )
                    row[column] = Convert.DBNull; 
                else 
                    SetRowValueFromXmlText( row, column, value );
            } 
            else
                fMore = iter.Next();

            // Fill up the columns mapped to an element 
            while ( fMore ) {
                XmlElement e = iter.CurrentNode as XmlElement; 
                if ( e == null ) { 
                    fMore = iter.Next();
                    continue; 
                }

                XmlBoundElement be = e as XmlBoundElement;
                if ( be != null && be.Row != null ) { 
                    if ( rowElemList != null )
                        rowElemList.Add( e ); 
                    // Skip over sub-regions 
                    fMore = iter.NextRight();
                    continue; 
                }

                DataColumn c = mapper.GetColumnSchemaForNode( rowElement, e );
                if ( c != null ) { 
                    Debug.Assert( c.Table == row.Table );
                    if ( foundColumns[c] == null ) { 
                        foundColumns[c] = c; 
                        string value;
                        fMore = iter.NextInitialTextLikeNodes( out value ); 
                        if ( value.Length == 0 && ( ( (xsi_attrVal = e.GetAttribute(XSI_NIL) ) == "1" ) || xsi_attrVal == "true" ) )
                            row[c] = Convert.DBNull;
                        else
                            SetRowValueFromXmlText( row, c, value ); 
                        continue;
                    } 
                } 

                fMore = iter.Next(); 
            }

            //
            // Walk the attributes to find attributes that map to columns. 
            //
            foreach( XmlAttribute attr in rowElement.Attributes ) { 
                DataColumn c = mapper.GetColumnSchemaForNode( rowElement, attr ); 

                if ( c != null ) { 
                    if ( foundColumns[c] == null ) {
                        foundColumns[c] = c;
                        SetRowValueFromXmlText( row, c, attr.Value );
                    } 
                }
            } 
 
            // Null all columns values that aren't represented in the tree
            foreach( DataColumn c in row.Table.Columns ) { 
                if ( foundColumns[c] == null && !IsNotMapped(c) ) {
                    if (!c.AutoIncrement)
                        SetRowValueToNull( row, c );
                    else 
                        c.Init(row.tempRecord);
                } 
            } 
        }
 
        private void UpdateAllColumns( DataRow row, XmlBoundElement rowElement ) {
            foreach( DataColumn c in row.Table.Columns ) {
                OnColumnValueChanged( row, c, rowElement );
            } 
        }
 
        ///  
        ///    
        ///       Initializes a new instance of the XmlDataDocument class. 
        ///    
        /// 
        public XmlDataDocument(): base(new XmlDataImplementation()) {
            Init(); 
            AttachDataSet( new DataSet() );
            this.dataSet.EnforceConstraints = false; 
        } 

        ///  
        ///    
        ///       Initializes a new instance of the XmlDataDocument class with the specified
        ///       DataSet.
        ///     
        /// 
        public XmlDataDocument( DataSet dataset ): base(new XmlDataImplementation()) { 
            Init( dataset ); 
        }
 
        internal XmlDataDocument( XmlImplementation imp ) : base( imp ) {
        }

        private void Init() { 
            this.pointers = new Hashtable();
            this.countAddPointer = 0; 
            this.columnChangeList = new ArrayList(); 
            this.ignoreDataSetEvents = false;
            this.isFoliationEnabled = true; 
            this.optimizeStorage = true;
            this.fDataRowCreatedSpecial = false;
            autoFoliationState = ElementState.StrongFoliation;
            fAssociateDataRow = true; //this needs to be true for newly created elements should have associated datarows 
            mapper = new DataSetMapper();
            this.foliationLock = new object(); 
            this.ignoreXmlEvents = true; 
            this.attrXml = CreateAttribute( "xmlns", "xml", XPathNodePointer.s_strReservedXmlns );
            this.attrXml.Value = XPathNodePointer.s_strReservedXml; 
            this.ignoreXmlEvents = false;
        }

        private void Init( DataSet ds ) { 
            if ( ds == null )
                throw new ArgumentException(Res.GetString(Res.DataDom_DataSetNull)); 
            Init(); 
            if ( ds.FBoundToDocument )
                throw new ArgumentException( Res.GetString(Res.DataDom_MultipleDataSet) ); 
            ds.FBoundToDocument = true;
            this.dataSet = ds;
            Bind(true);
        } 

        private bool IsConnected( XmlNode node ) { 
            while ( true ) { 
                if ( node == null )
                    return false; 
                if ( node == this )
                    return true;

                XmlAttribute attr = node as XmlAttribute; 
                if ( attr != null )
                    node = attr.OwnerElement; 
                else 
                    node = node.ParentNode;
            } 
        }
        private bool IsRowLive( DataRow row ) {
            return ( row.RowState & ( DataRowState.Added | DataRowState.Unchanged | DataRowState.Modified ) ) != 0;
        } 
        private static void SetNestedParentRow( DataRow childRow, DataRow parentRow ) {
            DataRelation rel = GetNestedParentRelation( childRow ); 
            //we should not set this row's parentRow if the table doesn't match. 
            if ( rel != null ) {
                if ( parentRow == null || rel.ParentKey.Table != parentRow.Table ) 
                    childRow.SetParentRow( null, rel );
                else
                   childRow.SetParentRow( parentRow, rel );
            } 
        }
 
        // A node (node) was inserted into the main tree (connected) from oldParent==null state 
        private void OnNodeInsertedInTree( XmlNode node ) {
            XmlBoundElement be; 
            ArrayList rowElemList = new ArrayList();
            if ( mapper.GetRegion( node, out be ) ) {
                //
                if ( be == node ) { 
                    OnRowElementInsertedInTree( be, rowElemList );
                } 
                else { 
                    OnNonRowElementInsertedInTree( node, be, rowElemList );
                } 
            }
            else {
                // We only need to [....] the embedded sub-regions
                TreeIterator iter = new TreeIterator( node ); 
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() )
                    rowElemList.Add( iter.CurrentNode ); 
            } 

            // Process subregions, so they make transition from disconnected to connected tree 
            while ( rowElemList.Count > 0 ) {
                Debug.Assert(rowElemList[0] != null && rowElemList[0] is XmlBoundElement);
                XmlBoundElement subRowElem = (XmlBoundElement)(rowElemList[0]);
                rowElemList.RemoveAt( 0 ); 
                // Expect rowElem to have a DataTable schema, since it is a sub-region
                Debug.Assert( subRowElem != null ); 
                OnRowElementInsertedInTree( subRowElem, rowElemList ); 
            }
 
            // Assert that all sub-regions are assoc w/ "live" rows
            AssertLiveRows( node );
        }
        // "node" was inserting into a disconnected tree from oldParent==null state 
        private void OnNodeInsertedInFragment( XmlNode node ) {
            XmlBoundElement be; 
            if ( mapper.GetRegion( node, out be ) ) { 
                if ( be == node ) {
                    Debug.Assert( ! IsRowLive( be.Row ) ); 
                    SetNestedParentRegion( be );
                }
                else {
                    ArrayList rowElemList = new ArrayList(); 
                    OnNonRowElementInsertedInFragment( node, be, rowElemList );
                    // Set nested parent for the 1st level subregions (they should already be associated w/ Deleted or Detached rows) 
                    while ( rowElemList.Count > 0 ) { 
                        Debug.Assert(rowElemList[0] != null && rowElemList[0] is XmlBoundElement);
                        XmlBoundElement subRowElem = (XmlBoundElement)(rowElemList[0]); 
                        rowElemList.RemoveAt( 0 );
                        SetNestedParentRegion( subRowElem, be );
                    }
                } 

                // Check to make sure all sub-regions are disconnected 
                AssertNonLiveRows( node ); 

                return; 
            }

            // Nothing to do, since the node belongs to no region
 
            // Check to make sure all sub-regions are disconnected
            AssertNonLiveRows( node ); 
        } 

        // A row-elem was inserted into the connected tree (connected) from oldParent==null state 
        private void OnRowElementInsertedInTree( XmlBoundElement rowElem, ArrayList rowElemList ) {
            Debug.Assert( rowElem.Row != null );

            DataRow row = rowElem.Row; 
            DataRowState rowState = row.RowState;
 
            switch( rowState ) { 
            case DataRowState.Detached:
#if DEBUG 
                try {
                    Debug.Assert( row.Table.DataSet.EnforceConstraints == false );
#endif
                    row.Table.Rows.Add( row ); 
                    SetNestedParentRegion( rowElem );
#if DEBUG 
                } 
                catch {
                    // We should not get any exceptions here 
                    Debug.Assert( false );
                    throw;
                }
#endif 
                // Add all sub-regions to the list if the caller needs this
                if ( rowElemList != null ) { 
                    RegionIterator iter = new RegionIterator( rowElem ); 
                    for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() )
                        rowElemList.Add( iter.CurrentNode ); 
                }
                break;
            case DataRowState.Deleted:
#if DEBUG 
                try {
                    Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 
#endif 
                    // Change the row status to be alive (unchanged)
                    row.RejectChanges(); 
                    // Set ROM from XML
                    SynchronizeRowFromRowElement( rowElem, rowElemList );
                    // Set nested parent data row according to where is the row positioned in the tree
                    SetNestedParentRegion( rowElem ); 
#if DEBUG
                } 
                catch { 
                    // We should not get any exceptions here
                    Debug.Assert( false ); 
                    throw;
                }
#endif
                break; 
            default:
                // Handle your case above 
                // 
                Debug.Assert( false );
                break; 
            }
            Debug.Assert( IsRowLive( rowElem.Row ) );
        }
 
        // Disconnect the DataRow associated w/ the rowElem region
        private void EnsureDisconnectedDataRow( XmlBoundElement rowElem ) { 
            Debug.Assert( rowElem.Row != null ); 

            DataRow row = rowElem.Row; 
            DataRowState rowState = row.RowState;

            switch( rowState ) {
            case DataRowState.Detached: 
#if DEBUG
                try { 
                    Debug.Assert( row.Table.DataSet.EnforceConstraints == false ); 
#endif
                    SetNestedParentRegion( rowElem ); 
#if DEBUG
                }
                catch {
                    // We should not get any exceptions here 
                    Debug.Assert( false );
                    throw; 
                } 
#endif
                break; 

            case DataRowState.Deleted:
                // Nothing to do: moving a region associated w/ a deleted row to another disconnected tree is a NO-OP.
                break; 

            case DataRowState.Unchanged: 
            case DataRowState.Modified: 
                EnsureFoliation( rowElem, ElementState.WeakFoliation );
                row.Delete(); 
                break;

            case DataRowState.Added:
                EnsureFoliation( rowElem, ElementState.WeakFoliation ); 
                row.Delete();
                SetNestedParentRegion( rowElem ); 
                break; 

            default: 
                // Handle your case above
                //
                Debug.Assert( false );
                break; 
            }
 
            Debug.Assert( ! IsRowLive( rowElem.Row ) ); 
        }
 

        // A non-row-elem was inserted into the connected tree (connected) from oldParent==null state
        private void OnNonRowElementInsertedInTree( XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList ) {
            // non-row-elem is beeing inserted 
            DataRow row = rowElement.Row;
            // Region should already have an associated data row (otherwise how was the original row-elem inserted ?) 
            Debug.Assert( row != null ); 
            SynchronizeRowFromRowElement( rowElement );
            if ( rowElemList != null ) { 
                TreeIterator iter = new TreeIterator( node );
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRightRowElement() )
                    rowElemList.Add( iter.CurrentNode );
            } 
        }
 
        // A non-row-elem was inserted into disconnected tree (fragment) from oldParent==null state (i.e. was disconnected) 
        private void OnNonRowElementInsertedInFragment( XmlNode node, XmlBoundElement rowElement, ArrayList rowElemList ) {
            // non-row-elem is beeing inserted 
            DataRow row = rowElement.Row;
            // Region should already have an associated data row (otherwise how was the original row-elem inserted ?)
            Debug.Assert( row != null );
            // Since oldParent == null, the only 2 row states should have been Detached or Deleted 
            Debug.Assert( row.RowState == DataRowState.Detached || row.RowState == DataRowState.Deleted );
 
            if ( row.RowState == DataRowState.Detached ) 
                SynchronizeRowFromRowElementEx( rowElement, rowElemList );
            // Nothing to do if the row is deleted (there is no [....]-ing from XML to ROM for deleted rows) 
        }

        private void SetNestedParentRegion( XmlBoundElement childRowElem ) {
            Debug.Assert( childRowElem.Row != null ); 

            XmlBoundElement parentRowElem; 
            mapper.GetRegion( childRowElem.ParentNode, out parentRowElem ); 
            SetNestedParentRegion( childRowElem, parentRowElem );
        } 
        private void SetNestedParentRegion( XmlBoundElement childRowElem, XmlBoundElement parentRowElem ) {
            DataRow childRow = childRowElem.Row;
            if ( parentRowElem == null ) {
                SetNestedParentRow( childRow, null ); 
                return;
            } 
 
            DataRow parentRow = parentRowElem.Row;
            Debug.Assert( parentRow != null ); 
            // We should set it only if there is a nested relationship between this child and parent regions
            DataRelation [] relations = childRow.Table.NestedParentRelations;
            if (relations.Length != 0 && relations[0].ParentTable == parentRow.Table ) // just backward compatable
                // 
                SetNestedParentRow( childRow, parentRow );
            else 
                SetNestedParentRow( childRow, null ); 
        }
 
        internal static bool IsTextNode( XmlNodeType nt ) {
            switch( nt ) {
                case XmlNodeType.Text:
                case XmlNodeType.CDATA: 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace: 
                    return true; 
                default:
                    return false; 
            }
        }

        /* 
        internal static bool IsWhiteSpace(char ch) {
            switch ( ch ) { 
                case '\u0009' : 
                case '\u000a' :
                case '\u000d' : 
                case '\u0020' :
                    return true;
                default :
                    return false; 
            }
        } 
 
        internal static bool IsOnlyWhitespace( string str ) {
            if (str != null) { 
                for (int index = 0; index < str.Length; index ++) {
                    if (! IsWhiteSpace(str[index]))
                        return false;
                } 
            }
            return true; 
        } 
        */
 
        /// 
        ///    [To be supplied.]
        /// 
        protected override XPathNavigator CreateNavigator(XmlNode node) { 
            Debug.Assert( node.OwnerDocument == this || node == this );
            if ( XPathNodePointer.xmlNodeType_To_XpathNodeType_Map[(int)(node.NodeType)] == -1 ) 
                return null; 
            if ( IsTextNode( node.NodeType ) ) {
                XmlNode parent = node.ParentNode; 
                if ( parent != null && parent.NodeType == XmlNodeType.Attribute )
                    return null;
                else {
#if DEBUG 
                    //if current node is a text node, its parent node has to be foliated
                    XmlBoundElement be = node.ParentNode as XmlBoundElement; 
                    if ( be != null ) 
                        Debug.Assert( be.IsFoliated );
#endif 
                    XmlNode prevSib = node.PreviousSibling;
                    while ( prevSib != null && IsTextNode( prevSib.NodeType ) ) {
                        node = prevSib;
                        prevSib = SafePreviousSibling( node ); 
                    }
                } 
            } 
            return new DataDocumentXPathNavigator( this, node );
        } 

        [System.Diagnostics.Conditional("DEBUG")]
        private void AssertLiveRows( XmlNode node ) {
            bool wasFoliationEnabled = IsFoliationEnabled; 
            IsFoliationEnabled = false;
            try { 
                XmlBoundElement rowElement = node as XmlBoundElement; 
                if ( rowElement != null && rowElement.Row != null )
                    Debug.Assert( IsRowLive( rowElement.Row ) ); 
                TreeIterator iter = new TreeIterator( node );
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement() ) {
                    rowElement = iter.CurrentNode as XmlBoundElement;
                    Debug.Assert( rowElement.Row != null ); 
                    Debug.Assert( IsRowLive( rowElement.Row ) );
                } 
            } 
            finally {
                IsFoliationEnabled = wasFoliationEnabled; 
            }
        }
        [System.Diagnostics.Conditional("DEBUG")]
        private void AssertNonLiveRows( XmlNode node ) { 
            bool wasFoliationEnabled = IsFoliationEnabled;
            IsFoliationEnabled = false; 
            try { 
                XmlBoundElement rowElement = node as XmlBoundElement;
                if ( rowElement != null && rowElement.Row != null ) 
                    Debug.Assert( ! IsRowLive( rowElement.Row ) );
                TreeIterator iter = new TreeIterator( node );
                for (bool fMore = iter.NextRowElement(); fMore; fMore = iter.NextRowElement() ) {
                    rowElement = iter.CurrentNode as XmlBoundElement; 
                    Debug.Assert( rowElement.Row != null );
                    Debug.Assert( ! IsRowLive( rowElement.Row ) ); 
                } 
            }
            finally { 
                IsFoliationEnabled = wasFoliationEnabled;
            }
        }
 
        public override XmlElement GetElementById( string elemId ) {
            throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_GetElementById ) ); 
        } 
        public override XmlNodeList GetElementsByTagName(string name) {
            // Retrieving nodes from the returned nodelist may cause foliation which causes new nodes to be created, 
            // so the System.Xml iterator will throw if this happens during iteration. To avoid this, foliate everything
            // before iteration, so iteration will not cause foliation (and as a result of this, creation of new nodes).
            XmlNodeList tempNodeList = base.GetElementsByTagName(name);
 
            int tempint = tempNodeList.Count;
            return tempNodeList; 
        } 

// Webdata 103397 

//  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
//  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
// so in order to maintain backward compatability, we reorder the copy of the datatable collection and use it 
        private DataTable[] OrderTables(DataSet ds) {
 
            DataTable[] retValue = null; 
            if (ds == null ||ds.Tables.Count == 0) {
                retValue = new DataTable[0]; 
            }
            else if (TablesAreOrdered(ds)) {
                retValue =  new DataTable[ds.Tables.Count];
                ds.Tables.CopyTo(retValue, 0); 
 		// XDD assumes PArent table exist before its child, if it does not we wont be handle the case
		// same as Everett 
            } 

            if (null == retValue) { 
                retValue =  new DataTable[ds.Tables.Count];
                List tableList = new List();
                // first take the root tables that have no parent
                foreach(DataTable dt in ds.Tables) { 
                    if (dt.ParentRelations.Count == 0) {
                        tableList.Add(dt); 
                    } 
                }
 
                if (tableList.Count > 0) { // if we have some  table inside;
                    foreach(DataTable dt in ds.Tables) {
                        if (IsSelfRelatedDataTable(dt)) {
                            tableList.Add(dt); 
                        }
                    } 
                    for(int readPos = 0 ; readPos < tableList.Count; readPos ++) { 
                        Debug.Assert(tableList[readPos] != null, "Temp Array is not supposed to reach to null");
                        foreach(DataRelation r in  tableList[readPos].ChildRelations) { 
                            DataTable childTable = r.ChildTable;
                            if (!tableList.Contains(childTable))
                                tableList.Add(childTable);
                        } 
                    }
                    tableList.CopyTo(retValue); 
 
                }
                else {//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 
                    ds.Tables.CopyTo(retValue, 0);
                }
            }
            return retValue; 
        }
        private bool IsSelfRelatedDataTable(DataTable rootTable) { 
            List tableList = new List(); 
            bool retValue = false;
            foreach(DataRelation r in  rootTable.ChildRelations) { 
                DataTable childTable = r.ChildTable;
                if (childTable == rootTable) {
                    retValue = true;
                    break; 
                }
                else if (!tableList.Contains(childTable)) { 
                    tableList.Add(childTable); 
                }
            } 
            if (!retValue) {
                for(int counter = 0 ; counter < tableList.Count; counter++) {
                    foreach(DataRelation r in  tableList[counter].ChildRelations) {
                        DataTable childTable = r.ChildTable; 
                        if (childTable == rootTable) {
                            retValue = true; 
                            break; 
                        }
                        else if (!tableList.Contains(childTable)) { 
                            tableList.Add(childTable);
                        }
                    }
                    if (retValue){ 
                        break;
                    } 
                } 
            }
            return retValue; 
        }
        private bool TablesAreOrdered(DataSet ds) {
            foreach(DataTable dt in ds.Tables){
                if (dt.Namespace != ds.Namespace) { 
                    return false;
                } 
            } 
            return true;
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK