2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Data / System.Data / XmlDiffLoader.cs
index e81ef3a7fbec589d1e19cc58cc900453b425f158..76a0adfa97241b9876d187697b64ee5a68940e8e 100644 (file)
@@ -9,12 +9,37 @@
 //
 // Author:
 //     Ville Palo <vi64pa@koti.soon.fi>
+//     Lluis Sanchez Gual (lluis@ximian.com)
 //
 // (c)copyright 2003 Ville Palo
 //
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
 using System;
 using System.Data;
 using System.Xml;
+using System.Xml.Schema;
 using System.Xml.XPath;
 using System.Collections;
 using System.Globalization;
@@ -25,8 +50,8 @@ namespace System.Data {
        {
 
                #region Fields
-               enum LoadType {CURRENT, BEFORE, ERROR};
                private DataSet DSet;
+               private DataTable table;
                private Hashtable DiffGrRows = new Hashtable ();
                private Hashtable ErrorRows = new Hashtable ();
 
@@ -39,242 +64,295 @@ namespace System.Data {
                        this.DSet = DSet;
                }
 
+               public XmlDiffLoader (DataTable table) 
+               {
+                       this.table = table;
+               }
+
                #endregion //ctors
 
                #region Public methods
 
-               public void Load (XmlReader Reader) 
+               public void Load (XmlReader reader) 
                {
-
-                       XmlDocument Document = BuildXmlDocument(Reader);
+                       bool origEnforceConstraint = false;
+                       if (DSet != null) {
+                               origEnforceConstraint = DSet.EnforceConstraints;
+                               DSet.EnforceConstraints = false;
+                       }
+                       
+                       reader.MoveToContent ();
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               return;
+                       }
+                       
+                       reader.ReadStartElement ("diffgram", XmlConstants.DiffgrNamespace);
+                       reader.MoveToContent ();
                        
-                       XPathNavigator Navigator = Document.CreateNavigator ();
-                       bool origEnforceConstraint = DSet.EnforceConstraints;
-                       DSet.EnforceConstraints = false;
-                       LoadBefore (Navigator);
-                       LoadCurrent (Navigator);
-                       LoadErrors (Navigator);
-                       DSet.EnforceConstraints = origEnforceConstraint;
+                       while (reader.NodeType != XmlNodeType.EndElement)
+                       {
+                               if (reader.NodeType == XmlNodeType.Element)
+                               {
+                                       if (reader.LocalName == "before" && reader.NamespaceURI == XmlConstants.DiffgrNamespace)
+                                               LoadBefore (reader);
+                                       else if (reader.LocalName == "errors" && reader.NamespaceURI == XmlConstants.DiffgrNamespace)
+                                               LoadErrors (reader);
+                                       else
+                                               LoadCurrent (reader);
+                               }
+                               else
+                                       reader.Skip ();
+                       }
+                       
+                       reader.ReadEndElement ();
+                       
+                       if (DSet != null)
+                               DSet.EnforceConstraints = origEnforceConstraint;
                }
 
                #endregion // Public methods
 
                #region Private methods
 
-               private void LoadCurrent (XPathNavigator Navigator) 
-               {                       
-                       Navigator.MoveToRoot ();
-
-                       if (Navigator.MoveToFirstChild ()) {
-
-                               if (Navigator.Name == "diffgr:diffgram") {
-
-                                       if (Navigator.MoveToFirstChild ()) {
+               private void LoadCurrent (XmlReader reader) 
+               {
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               return;
+                       }
+                       reader.ReadStartElement ();             // Dataset root
+                       reader.MoveToContent ();
 
-                                               if (Navigator.MoveToFirstChild ()) {
-                                                       do {
-                                                               if (DSet.Tables.Contains (Navigator.LocalName))
-                                                                       LoadCurrentTable(Navigator);
-                                                               else 
-                                                                       throw new DataException (Locale.GetText ("Cannot load diffGram. Table '" + Navigator.LocalName + "' is missing in the destination dataset"));   
-                                                       }while (Navigator.MoveToNext());
-                                               }
-                                       }
+                       while (reader.NodeType != XmlNodeType.EndElement)
+                       {
+                               if (reader.NodeType == XmlNodeType.Element)
+                               {
+                                       DataTable t = GetTable (reader.LocalName);
+                                       if (t != null)
+                                               LoadCurrentTable (t, reader);
+#if true
+                                       else
+                                               reader.Skip ();
+#else
+                                       else 
+                                               throw new DataException (Locale.GetText ("Cannot load diffGram. Table '" + reader.LocalName + "' is missing in the destination dataset"));      
+#endif
                                }
+                               else
+                                       reader.Skip ();
                        }
+                       
+                       reader.ReadEndElement ();
                }
 
-               private void LoadBefore (XPathNavigator Navigator) 
+               private void LoadBefore (XmlReader reader) 
                {
-                       Navigator.MoveToRoot ();
-
-                       if (!Navigator.MoveToFirstChild ())
-                               return; // FIXME: exception
-                       
-                       if (Navigator.Name != "diffgr:diffgram")
-                               return; // FIXME: exception
-
-                       if (Navigator.MoveToFirstChild ()) {
-
-                               while (Navigator.Name != "diffgr:before") {
-
-                                       if (!Navigator.MoveToNext ()) // there is no before
-                                               return;
-                               }
-
-                               if (Navigator.MoveToFirstChild ()) {
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               return;
+                       }
+                       reader.ReadStartElement ();
+                       reader.MoveToContent ();
 
-                                       do {
-                                               if (DSet.Tables.Contains (Navigator.LocalName))
-                                                       LoadBeforeTable(Navigator);
-                                               else
-                                                       throw new DataException (Locale.GetText ("Cannot load diffGram. Table '" + Navigator.LocalName + "' is missing in the destination dataset"));
-                                       } while (Navigator.MoveToNext ());
+                       while (reader.NodeType != XmlNodeType.EndElement)
+                       {
+                               if (reader.NodeType == XmlNodeType.Element)
+                               {
+                                       DataTable t = GetTable (reader.LocalName);
+                                       if (t != null)
+                                               LoadBeforeTable(t, reader);
+                                       else
+                                               throw new DataException (Locale.GetText ("Cannot load diffGram. Table '" + reader.LocalName + "' is missing in the destination dataset"));
                                }
+                               else
+                                       reader.Skip ();
                        }
+                       
+                       reader.ReadEndElement ();
                }                                
                                
                                           
-               private void LoadErrors (XPathNavigator Navigator) 
+               private void LoadErrors (XmlReader reader) 
                {
-                       Navigator.MoveToRoot ();
-
-                       if (!Navigator.MoveToFirstChild ())
-                               return; // FIXME: exception
-                       
-                       if (Navigator.Name != "diffgr:diffgram")
-                               return; // FIXME: exception
-
-                       if (Navigator.MoveToFirstChild ()) {
-                               
-                               while (Navigator.Name != "diffgr:errors") {
-                                       if (!Navigator.MoveToNext ())
-                                               return;
-                               }
-
-                               if (Navigator.MoveToFirstChild ()) {
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               return;
+                       }
+                       reader.ReadStartElement ();
+                       reader.MoveToContent ();
 
+                       while (reader.NodeType != XmlNodeType.EndElement)
+                       {
+                               if (reader.NodeType == XmlNodeType.Element)
+                               {
                                        DataRow Row = null;
-
+               
                                        // find the row in 'current' section
-                                       if (Navigator.MoveToFirstAttribute ()) {
-
-                                               do {
-                                                       if (Navigator.Name == "diffgr:id") {
-                                                               
-                                                               if (ErrorRows.Contains (Navigator.Value))
-                                                                       Row = (DataRow)ErrorRows [Navigator.Value];
-                                                       }
-
-                                               } while (Navigator.MoveToNextAttribute ());
-                                               
-                                               Navigator.MoveToParent ();                                              
-                                       }
-
-                                       if (Navigator.MoveToFirstChild ()) {
-
-                                               string Error = "";
-                                               
-                                               do {
-                                                       if (Navigator.MoveToFirstAttribute ()) {
-                                                               do {
-                                                                       if (Navigator.Name == "diffgr:Error")
-                                                                               Error = Navigator.Value;
-
-                                                               } while (Navigator.MoveToNextAttribute ());
-                                                               
-                                                               Navigator.MoveToParent ();
-                                                       }
-
-                                                       Row.SetColumnError (Navigator.LocalName, Error);
-
-                                               } while (Navigator.MoveToNext ());
+                                       
+                                       string id = reader.GetAttribute ("id", XmlConstants.DiffgrNamespace);
+                                       
+                                       if (id != null)
+                                               Row = (DataRow) ErrorRows [id];
+               
+                                       if (reader.IsEmptyElement) continue;
+                                       reader.ReadStartElement ();
+                                       while (reader.NodeType != XmlNodeType.EndElement)
+                                       {
+                                               if (reader.NodeType == XmlNodeType.Element) {
+                                                       string error = reader.GetAttribute ("Error", XmlConstants.DiffgrNamespace);
+                                                       Row.SetColumnError (reader.LocalName, error);
+                                               }
+                                               reader.Read ();
                                        }
                                }
+                               else
+                                       reader.Skip ();
                        }
+                       reader.ReadEndElement ();
                }
 
-               private void LoadColumns (DataTable Table, DataRow Row, XPathNavigator Navigator, bool NewRow, LoadType loadType) 
+               private void LoadColumns (DataTable Table, DataRow Row, 
+                       XmlReader reader, DataRowVersion loadType)
                {
-                       if (Navigator.MoveToFirstChild ()) {
-
-                               do {
-                                       if (Table.Columns.Contains (Navigator.LocalName))
-                                               Row [Navigator.LocalName] = XmlDataLoader.StringToObject(Table.Columns[Navigator.LocalName].DataType, Navigator.Value);
-                                       else if (DSet.Tables.Contains (Navigator.LocalName)){
-                                               if (loadType == LoadType.BEFORE)
-                                                       LoadBeforeTable(Navigator);
-                                               else if (loadType == LoadType.CURRENT)
-                                                       LoadCurrentTable(Navigator);
-                                       }
-                                                                               
-                               } while (Navigator.MoveToNext ());
-                               
-                               if (NewRow)
-                                       Table.Rows.Add (Row);
-                       }
+                       // attributes
+                       LoadColumnAttributes (Table, Row, reader, loadType);
+                       LoadColumnChildren (Table, Row, reader, loadType);
                }
 
-               private void LoadBeforeTable (XPathNavigator Navigator) 
+               private void LoadColumnAttributes (DataTable Table, DataRow Row,
+                       XmlReader reader, DataRowVersion loadType)
                {
-                               
-                       String id = null;
-                       DataTable Table = DSet.Tables [Navigator.LocalName];
-                       DataRow Row = Table.NewRow ();
-                                                       
-                       if (Navigator.MoveToFirstAttribute ()) {
-                                                               
-                               do {
-                                       if (Navigator.Name == "diffgr:id")
-                                               id = Navigator.Value;
-                                                                      
-                               } while (Navigator.MoveToNextAttribute ());
-                                                               
-                               Navigator.MoveToParent ();
-                       }
-                                                                                                               
-                       LoadColumns (Table, Row, Navigator, true, LoadType.BEFORE);
-                       DiffGrRows.Add (id, Row); // for later use
-                       Row.AcceptChanges ();
+                       if (!reader.HasAttributes // this check will be faster
+                               || !reader.MoveToFirstAttribute ())
+                               return;
+                       do {
+                               switch (reader.NamespaceURI) {
+                               case XmlConstants.XmlnsNS:
+                               case XmlConstants.DiffgrNamespace:
+                               case XmlConstants.MsdataNamespace:
+                               case XmlConstants.MspropNamespace:
+                               case XmlSchema.Namespace:
+                                       continue;
+                               }
+                               DataColumn c = Table.Columns [reader.LocalName];
+                               if (c == null ||
+                                       c.ColumnMapping != MappingType.Attribute)                                       continue;
+                               if (c.Namespace == null && reader.NamespaceURI == String.Empty ||
+                                       c.Namespace == reader.NamespaceURI) {
+                                       object data = XmlDataLoader.StringToObject (c.DataType, reader.Value);
+                                       if (loadType == DataRowVersion.Current)
+                                               Row [c] = data;
+                                       else
+                                               Row.SetOriginalValue (c.ColumnName, data);
+                               } // otherwise just ignore as well as unknown elements.
+                       } while (reader.MoveToNextAttribute ());
+                       reader.MoveToElement ();
                }
 
-               private void LoadCurrentTable (XPathNavigator Navigator) 
+               private void LoadColumnChildren (DataTable Table, DataRow Row,
+                       XmlReader reader, DataRowVersion loadType) 
                {
+                       // children
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               return;
+                       }
+                       reader.ReadStartElement ();
+                       reader.MoveToContent ();
                        
-                       DataTable Table = DSet.Tables [Navigator.LocalName];
-                       DataRow Row = null;
-                       bool NewRow = false;
-                       bool HasErrors = false;
-                       string id = "";
-                                                               
-                       if (Navigator.MoveToFirstAttribute ()) {
-                                                                       
-                               do {
-                                       // Find out was there same row in 'before' section
-                                       if (Navigator.LocalName == "id") {
-                                               id = Navigator.Value;
-                                               if (DiffGrRows.Contains (id))
-                                                       Row = (DataRow)DiffGrRows [id];
-
+                       while (reader.NodeType != XmlNodeType.EndElement)
+                       {
+                               if (reader.NodeType != XmlNodeType.Element) { reader.Read (); continue; }
+                               
+                               if (Table.Columns.Contains (reader.LocalName)) 
+                               {
+                                       string colName = reader.LocalName;
+                                       object data = XmlDataLoader.StringToObject (Table.Columns[colName].DataType, reader.ReadString ());
+                                       
+                                       if (loadType == DataRowVersion.Current) Row [colName] = data;
+                                       else Row.SetOriginalValue (colName, data);
+                                       reader.Read ();
+                               }
+                               else 
+                               {
+                                       DataTable t = GetTable (reader.LocalName);
+                                       if (t != null) {
+                                               if (loadType == DataRowVersion.Original)
+                                                       LoadBeforeTable (t, reader);
+                                               else if (loadType == DataRowVersion.Current)
+                                                       LoadCurrentTable (t, reader);
                                        }
-                                       else if (Navigator.LocalName == "hasErrors" && String.Compare (Navigator.Value, "true", true) == 0)
-                                               HasErrors = true;
-                               } while (Navigator.MoveToNextAttribute ());
-
-                               // back to business
-                               Navigator.MoveToParent ();
+                               }
                        }
+                       
+                       reader.ReadEndElement ();
+               }
 
-                       if (Row == null) {
+               private void LoadBeforeTable (DataTable Table, XmlReader reader) 
+               {
+                       string id = reader.GetAttribute ("id", XmlConstants.DiffgrNamespace);
+                       string rowOrder = reader.GetAttribute ("rowOrder", XmlConstants.MsdataNamespace);
+                       DataRow Row = (DataRow) DiffGrRows [id];
+                       
+                       if (Row == null)
+                       {
+                               // Deleted row
                                Row = Table.NewRow ();
-                               NewRow = true;
+                               LoadColumns (Table, Row, reader, DataRowVersion.Current);
+                               Table.Rows.InsertAt (Row, int.Parse (rowOrder));
+                               Row.AcceptChanges ();
+                               Row.Delete ();
+                       }
+                       else
+                       {
+                               LoadColumns (Table, Row, reader, DataRowVersion.Original);
                        }
-                                                                                                                               
-                       LoadColumns (Table, Row, Navigator, NewRow, LoadType.CURRENT);
-                                                                       
-                       // back to business
-                       Navigator.MoveToParent();
-                                                                       
-                       if (HasErrors) // If row had errors add row to hashtable for later use
-                               ErrorRows.Add (id, Row);
-               
                }
 
-               private static XmlDocument BuildXmlDocument(XmlReader reader)
+               private void LoadCurrentTable (DataTable Table, XmlReader reader) 
                {
-                       string endinglocalName = reader.LocalName;
-
-                       XmlDocument doc = new XmlDocument();
+                       DataRowState state;
+                       DataRow Row = Table.NewRow ();
 
-                       // create all contents with use of ReadNode()
-                       do 
+                       string id = reader.GetAttribute  ("id", XmlConstants.DiffgrNamespace);
+                       string error = reader.GetAttribute ("hasErrors");
+                       string changes = reader.GetAttribute ("hasChanges", XmlConstants.DiffgrNamespace);
+                       
+                       if (changes != null)
                        {
-                               XmlNode n = doc.ReadNode (reader);
-                               if(n == null) break;
-                               doc.AppendChild (n);
-                       } while (reader.LocalName == endinglocalName);
+                               if (string.Compare (changes, "modified", true) == 0) {
+                                       DiffGrRows.Add (id, Row); // for later use
+                                       state = DataRowState.Modified;
+                               }
+                               else if (string.Compare (changes, "inserted", true) == 0) {
+                                       state = DataRowState.Added;
+                               }
+                               else
+                                       throw new InvalidOperationException ("Invalid row change state");
+                       }
+                       else
+                               state = DataRowState.Unchanged;
+                       
+                       // If row had errors add row to hashtable for later use
+                       if (error != null && string.Compare (error, "true", true) == 0)
+                               ErrorRows.Add (id, Row);
+               
+                       LoadColumns (Table, Row, reader, DataRowVersion.Current);
+                       Table.Rows.Add (Row);
+                       
+                       if (state != DataRowState.Added)
+                               Row.AcceptChanges ();
+               }
 
-                       return doc;
+               DataTable GetTable (string name)
+               {
+                       if (DSet != null) 
+                               return DSet.Tables [name];
+                       else if (name == table.TableName) 
+                               return table;
+                       else
+                               return null;
                }