| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827 |
- //------------------------------------------------------------------------------
- // <copyright file="XmlDataDocument.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">Microsoft</owner>
- // <owner current="true" primary="false">Microsoft</owner>
- //------------------------------------------------------------------------------
- namespace System.Xml {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.IO;
- using System.Runtime.Versioning;
- using System.Xml.XPath;
- /// <devdoc>
- /// <para>
- /// Represents an entire document. An XmlDataDocument can contain XML
- /// data or relational data (DataSet).
- /// </para>
- /// </devdoc>
- [Obsolete("XmlDataDocument class will be removed in a future release.")]
- [System.Security.Permissions.HostProtectionAttribute(Synchronization=true)]
- public 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 );
- }
- }
- /// <devdoc>
- /// <para>
- /// Creates an element with the specified Prefix, LocalName, and
- /// NamespaceURI.
- /// </para>
- /// </devdoc>
- 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 );
- }
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- public override XmlEntityReference CreateEntityReference(String name) {
- throw new NotSupportedException( Res.GetString(Res.DataDom_NotSupport_EntRef ) );
- }
- /// <devdoc>
- /// <para>Gets a DataSet that provides a relational representation of the data in this
- /// XmlDataDocument.</para>
- /// </devdoc>
- 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 sync w/ temp values
- // For non-detached rows: we are in sync w/ the current values
- // For deleted rows: we never sync
- 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;
- }
- /// <devdoc>
- /// <para>
- /// <SPAN>Retrieves the
- /// DataRow associated with the specified XmlElement.</SPAN>
- /// </para>
- /// </devdoc>
- 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;
- }
- /// <devdoc>
- /// <para><SPAN>Retrieves
- /// the XmlElement associated with the specified DataRow.</SPAN></para>
- /// </devdoc>
- 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;
- }
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- 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;
- }
- /// <devdoc>
- /// <para>Loads the XML document from the specified file.</para>
- /// </devdoc>
- [ResourceExposure(ResourceScope.Machine)]
- [ResourceConsumption(ResourceScope.Machine)]
- public override void Load(string filename) {
- this.bForceExpandEntity = true;
- base.Load( filename );
- this.bForceExpandEntity = false;
- }
- /// <devdoc>
- /// <para>Loads the XML document from the specified Stream.</para>
- /// </devdoc>
- public override void Load( Stream inStream ) {
- this.bForceExpandEntity = true;
- base.Load( inStream );
- this.bForceExpandEntity = false;
- }
- /// <devdoc>
- /// <para>Loads the XML document from the specified TextReader.</para>
- /// </devdoc>
- public override void Load( TextReader txtReader ) {
- this.bForceExpandEntity = true;
- base.Load( txtReader );
- this.bForceExpandEntity = false;
- }
- /// <devdoc>
- /// <para>Loads the XML document from the specified XmlReader.</para>
- /// </devdoc>
- 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 sync 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 sync 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 sync-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 sync 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 ) ) {
- // Sync 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 );
- }
- // Sync 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 );
- }
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the XmlDataDocument class.
- /// </para>
- /// </devdoc>
- public XmlDataDocument(): base(new XmlDataImplementation()) {
- Init();
- AttachDataSet( new DataSet() );
- this.dataSet.EnforceConstraints = false;
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the XmlDataDocument class with the specified
- /// DataSet.
- /// </para>
- /// </devdoc>
- 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 sync 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 sync-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;
- }
- */
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- 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<DataTable> tableList = new List<DataTable>();
- // 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<DataTable> tableList = new List<DataTable>();
- 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;
- }
- }
- }
|