XmlDataDocument.cs 125 KB

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