2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Data / System.Data / DataSet.cs
index 727e866a34f50a87192da52e01155f25afaffaa4..17815bb815c8470fbe16b9a96b5177be529e6eb6 100644 (file)
@@ -347,11 +347,17 @@ namespace System.Data {
 
                public virtual DataSet Clone ()
                {
-                       DataSet Copy = new DataSet ();
+                       // need to return the same type as this...
+                       DataSet Copy = (DataSet) Activator.CreateInstance(GetType(), true);
+       
                        CopyProperties (Copy);
 
                        foreach (DataTable Table in Tables) {
-                               Copy.Tables.Add (Table.Clone ());
+                       // tables are often added in no-args constructor, don't add them
+                       // twice.
+                               if (!Copy.Tables.Contains(Table.TableName)) {
+                                       Copy.Tables.Add (Table.Clone ());
+                               }
                        }
 
                        //Copy Relationships between tables after existance of tables
@@ -490,8 +496,8 @@ namespace System.Data {
                        }
                
                        DataRow newRow = copyTable.NewRow ();
-                       copyTable.Rows.Add (newRow);
                        row.CopyValuesToRow (newRow);
+                       copyTable.Rows.Add (newRow);
                        newRow.XmlRowID = row.XmlRowID;
                        addedRows.Add (row,row);
                }
@@ -857,117 +863,221 @@ namespace System.Data {
                        return ReadXml (new XmlTextReader (reader), mode);
                }
 
-               public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
+               // LAMESPEC: XmlReadMode.Fragment is far from presisely
+               // documented. MS.NET infers schema against this mode.
+               public XmlReadMode ReadXml (XmlReader input, XmlReadMode mode)
                {
-                       switch (reader.ReadState) {
+                       switch (input.ReadState) {
                        case ReadState.EndOfFile:
                        case ReadState.Error:
                        case ReadState.Closed:
                                return mode;
                        }
                        // Skip XML declaration and prolog
-                       reader.MoveToContent();
-                       if (reader.EOF)
+                       input.MoveToContent ();
+                       if (input.EOF)
                                return mode;
 
-                       XmlReadMode Result = mode;
+                       // FIXME: We need more decent code here, but for now
+                       // I don't know the precise MS.NET behavior, I just
+                       // delegate to specific read process.
+                       switch (mode) {
+                       case XmlReadMode.IgnoreSchema:
+                               return ReadXmlIgnoreSchema (input, mode, true);
+                       case XmlReadMode.ReadSchema:
+                               return ReadXmlReadSchema (input, mode, true);
+                       }
+                       // remaining modes are: Auto, InferSchema, Fragment, Diffgram
 
-                       // If diffgram, then read the first element as diffgram 
-                       if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
-                               switch (mode) {
-                               case XmlReadMode.Auto:
-                               case XmlReadMode.DiffGram:
-                                       XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
-                                       DiffLoader.Load (reader);
-                                       // (and leave rest of the reader as is)
-                                       return  XmlReadMode.DiffGram;
-                               case XmlReadMode.Fragment:
-                                       reader.Skip ();
-                                       // (and continue to read)
-                                       break;
-                               default:
-                                       reader.Skip ();
-                                       // (and leave rest of the reader as is)
-                                       return mode;
+                       XmlReader reader = input;
+
+                       int depth = reader.Depth;
+                       XmlReadMode result = mode;
+                       bool skippedTopLevelElement = false;
+                       string potentialDataSetName = null;
+                       XmlDocument doc = null;
+                       bool shouldReadData = mode != XmlReadMode.DiffGram;
+                       bool shouldNotInfer = Tables.Count > 0;
+
+                       switch (mode) {
+                       case XmlReadMode.Auto:
+                       case XmlReadMode.InferSchema:
+                               doc = new XmlDocument ();
+                               do {
+                                       doc.AppendChild (doc.ReadNode (reader));
+                               } while (!reader.EOF &&
+                                       doc.DocumentElement == null);
+                               reader = new XmlNodeReader (doc);
+                               reader.MoveToContent ();
+                               break;
+                       case XmlReadMode.DiffGram:
+                               if (!(reader.LocalName == "diffgram" &&
+                                       reader.NamespaceURI == XmlConstants.DiffgrNamespace))
+                                       goto case XmlReadMode.Auto;
+                               break;
+                       }
+
+                       switch (mode) {
+                       case XmlReadMode.Auto:
+                       case XmlReadMode.InferSchema:
+                       case XmlReadMode.ReadSchema:
+                               if (!(reader.LocalName == "diffgram" &&
+                                       reader.NamespaceURI == XmlConstants.DiffgrNamespace) &&
+                                       !(reader.LocalName == "schema" &&
+                                       reader.NamespaceURI == XmlSchema.Namespace))
+                                       potentialDataSetName = reader.LocalName;
+                               goto default;
+                       case XmlReadMode.Fragment:
+                               break;
+                       default:
+                               if (!(reader.LocalName == "diffgram" &&
+                                       reader.NamespaceURI == XmlConstants.DiffgrNamespace) &&
+                                       !(reader.LocalName == "schema" &&
+                                       reader.NamespaceURI == XmlSchema.Namespace)) {
+                                       if (!reader.IsEmptyElement) {
+                                               reader.Read ();
+                                               reader.MoveToContent ();
+                                               skippedTopLevelElement = true;
+                                       }
+                                       else {
+                                               switch (mode) {
+                                               case XmlReadMode.Auto:
+                                               case XmlReadMode.InferSchema:
+                                                       DataSetName = reader.LocalName;
+                                                       break;
+                                               }
+                                               reader.Read ();
+                                       }
                                }
+                               break;
                        }
+
                        // If schema, then read the first element as schema 
                        if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
+                               shouldNotInfer = true;
                                switch (mode) {
                                case XmlReadMode.IgnoreSchema:
                                case XmlReadMode.InferSchema:
                                        reader.Skip ();
-                                       // (and break up read)
-                                       return mode;
+                                       break;
                                case XmlReadMode.Fragment:
                                        ReadXmlSchema (reader);
-                                       // (and continue to read)
                                        break;
+                               case XmlReadMode.DiffGram:
                                case XmlReadMode.Auto:
                                        if (Tables.Count == 0) {
                                                ReadXmlSchema (reader);
-                                               return XmlReadMode.ReadSchema;
+                                               if (mode == XmlReadMode.Auto)
+                                                       result = XmlReadMode.ReadSchema;
                                        } else {
                                        // otherwise just ignore and return IgnoreSchema
                                                reader.Skip ();
-                                               return XmlReadMode.IgnoreSchema;
+                                               result = XmlReadMode.IgnoreSchema;
                                        }
-                               default:
+                                       break;
+                               case XmlReadMode.ReadSchema:
                                        ReadXmlSchema (reader);
-                                       // (and leave rest of the reader as is)
-                                       return mode; // When DiffGram, return DiffGram
+                                       break;
+                               }
+                       }
+
+                       // If diffgram, then read the first element as diffgram 
+                       if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
+                               switch (mode) {
+                               case XmlReadMode.Auto:
+                               case XmlReadMode.IgnoreSchema:
+                               case XmlReadMode.DiffGram:
+                                       XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
+                                       DiffLoader.Load (reader);
+                                       if (mode == XmlReadMode.Auto)
+                                               result = XmlReadMode.DiffGram;
+                                       shouldReadData = false;
+                                       break;
+                               case XmlReadMode.Fragment:
+                                       reader.Skip ();
+                                       break;
+                               default:
+                                       reader.Skip ();
+                                       break;
                                }
                        }
+
+                       // if schema after diffgram, just skip it.
+                       if (!shouldReadData && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
+                               shouldNotInfer = true;
+                               switch (mode) {
+                               default:
+                                       reader.Skip ();
+                                       break;
+                               case XmlReadMode.ReadSchema:
+                               case XmlReadMode.DiffGram:
+                                       if (Tables.Count == 0)
+                                               ReadXmlSchema (reader);
+                                       break;
+                               }
+                       }
+
+                       if (reader.EOF)
+                               return result == XmlReadMode.Auto ?
+                                       potentialDataSetName != null && !shouldNotInfer ?
+                                       XmlReadMode.InferSchema :
+                                       XmlReadMode.IgnoreSchema : result;
+
                        // Otherwise, read as dataset... but only when required.
-                       XmlReadMode explicitReturnMode = XmlReadMode.Auto;
-                       XmlDocument doc;
-                       switch (mode) {
-                       case XmlReadMode.Auto:
-                               if (Tables.Count > 0)
-                                       goto case XmlReadMode.IgnoreSchema;
-                               else
-                                       goto case XmlReadMode.InferSchema;
-                       case XmlReadMode.InferSchema:
-                               doc = new XmlDocument ();
-                               do {
-                                       doc.AppendChild (doc.ReadNode (reader));
-                                       reader.MoveToContent ();
-                                       if (doc.DocumentElement != null)
-                                               break;
-                               } while (!reader.EOF);
-                               InferXmlSchema (doc, null);
-                               reader = new XmlNodeReader (doc);
-                               explicitReturnMode = XmlReadMode.InferSchema;
-                               break;
-                       case XmlReadMode.ReadSchema:
-                               doc = new XmlDocument ();
-                               do {
-                                       doc.AppendChild (doc.ReadNode (reader));
-                                       reader.MoveToContent ();
-                                       if (doc.DocumentElement != null)
-                                               break;
-                               } while (!reader.EOF);
-                               if (doc.DocumentElement != null) {
-                                       XmlElement schema = doc.DocumentElement ["schema", XmlSchema.Namespace] as XmlElement;
-                                       if (schema != null) {
-                                               ReadXmlSchema (new XmlNodeReader (schema));
-                                               explicitReturnMode = XmlReadMode.ReadSchema;
-                                       }
+                       if (shouldReadData && !shouldNotInfer) {
+                               switch (mode) {
+                               case XmlReadMode.Auto:
+                                       if (Tables.Count > 0)
+                                               goto case XmlReadMode.IgnoreSchema;
+                                       else
+                                               goto case XmlReadMode.InferSchema;
+                               case XmlReadMode.InferSchema:
+                                       InferXmlSchema (doc, null);
+                                       if (mode == XmlReadMode.Auto)
+                                               result = XmlReadMode.InferSchema;
+                                       break;
+                               case XmlReadMode.IgnoreSchema:
+                               case XmlReadMode.Fragment:
+                               case XmlReadMode.DiffGram:
+                                       break;
+                               default:
+                                       shouldReadData = false;
+                                       break;
                                }
-                               reader = new XmlNodeReader (doc);
-                               break;
-                       case XmlReadMode.IgnoreSchema:
-                       case XmlReadMode.Fragment:
-                               break;
-                       default:
-                               reader.Skip ();
-                               return mode;
                        }
 
-                       XmlDataReader.ReadXml (this, reader, mode);
-                       if (explicitReturnMode != XmlReadMode.Auto)
-                               return explicitReturnMode;
-                       return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
+                       if (shouldReadData) {
+                               XmlReader dataReader = reader;
+                               if (doc != null) {
+                                       dataReader = new XmlNodeReader (doc);
+                                       dataReader.MoveToContent ();
+                               }
+                               if (reader.NodeType == XmlNodeType.Element)
+                                       XmlDataReader.ReadXml (this, dataReader,
+                                               mode);
+                       }
+
+                       if (skippedTopLevelElement) {
+                               switch (result) {
+                               case XmlReadMode.Auto:
+                               case XmlReadMode.InferSchema:
+//                                     DataSetName = potentialDataSetName;
+//                                     result = XmlReadMode.InferSchema;
+                                       break;
+                               }
+                               if (reader.NodeType == XmlNodeType.EndElement)
+                                       reader.ReadEndElement ();
+                       }
+//*
+                       while (input.Depth > depth)
+                               input.Read ();
+                       if (input.NodeType == XmlNodeType.EndElement)
+                               input.Read ();
+//*/
+                       input.MoveToContent ();
+
+                       return result == XmlReadMode.Auto ?
+                               XmlReadMode.IgnoreSchema : result;
                }
                #endregion // Public Methods
 
@@ -1041,14 +1151,7 @@ namespace System.Data {
                
                protected virtual void ReadXmlSerializable (XmlReader reader)
                {
-                       reader.MoveToContent ();
-                       reader.ReadStartElement ();
-                       reader.MoveToContent ();
-                       ReadXmlSchema (reader);
-                       reader.MoveToContent ();
-                       ReadXml (reader, XmlReadMode.DiffGram);
-                       reader.MoveToContent ();
-                       reader.ReadEndElement ();
+                       ReadXml (reader);
                }
 
                void IXmlSerializable.ReadXml (XmlReader reader)
@@ -1064,7 +1167,7 @@ namespace System.Data {
 
                XmlSchema IXmlSerializable.GetSchema ()
                {
-                       return BuildSchema ();
+                       return GetSchemaSerializable ();
                }
 
                protected virtual bool ShouldSerializeRelations ()
@@ -1104,7 +1207,59 @@ namespace System.Data {
                }
                #endregion
 
-               #region Private Xml Serialisation
+               #region Private Methods
+
+               private XmlReadMode ReadXmlIgnoreSchema (XmlReader input, XmlReadMode mode, bool checkRecurse)
+               {
+                       if (input.LocalName == "schema" &&
+                               input.NamespaceURI == XmlSchema.Namespace) {
+                               input.Skip ();
+                       }
+                       else if (input.LocalName == "diffgram" &&
+                               input.NamespaceURI == XmlConstants.DiffgrNamespace) {
+                               XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
+                               DiffLoader.Load (input);
+                       }
+                       else if (checkRecurse ||
+                               input.LocalName == DataSetName &&
+                               input.NamespaceURI == Namespace) {
+                               XmlDataReader.ReadXml (this, input, mode);
+                       }
+                       else if (checkRecurse && !input.IsEmptyElement) {
+                               int depth = input.Depth;
+                               input.Read ();
+                               input.MoveToContent ();
+                               ReadXmlIgnoreSchema (input, mode, false);
+                               while (input.Depth > depth)
+                                       input.Skip ();
+                               if (input.NodeType == XmlNodeType.EndElement)
+                                       input.ReadEndElement ();
+                       }
+                       input.MoveToContent ();
+                       return XmlReadMode.IgnoreSchema;
+               }
+
+               private XmlReadMode ReadXmlReadSchema (XmlReader input, XmlReadMode mode, bool checkRecurse)
+               {
+                       if (input.LocalName == "schema" &&
+                               input.NamespaceURI == XmlSchema.Namespace) {
+                               ReadXmlSchema (input);
+                       }
+                       else if (checkRecurse && !input.IsEmptyElement) {
+                               int depth = input.Depth;
+                               input.Read ();
+                               input.MoveToContent ();
+                               ReadXmlReadSchema (input, mode, false);
+                               while (input.Depth > depth)
+                                       input.Skip ();
+                               if (input.NodeType == XmlNodeType.EndElement)
+                                       input.ReadEndElement ();
+                       }
+                       else
+                               input.Skip ();
+                       input.MoveToContent ();
+                       return XmlReadMode.ReadSchema;
+               }
 
                private string WriteObjectXml (object o)
                {
@@ -1391,19 +1546,21 @@ namespace System.Data {
                        }
 
                        // set the schema id
-                       string xmlNSURI = "http://www.w3.org/2000/xmlns/";
                        schema.Id = DataSetName;
                        XmlDocument doc = new XmlDocument ();
                        XmlAttribute attr = null;
                        ArrayList atts = new ArrayList ();
 
+                       attr = doc.CreateAttribute ("", "xmlns", XmlConstants.XmlnsNS);
+                       atts.Add (attr);
+
                        nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
                        nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
                        if (Namespace != "") {
                                nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
                                nsmgr.AddNamespace (String.Empty, Namespace);
                        }
-                       if (CheckExtendedPropertyExists ())
+                       if (CheckExtendedPropertyExists (tables, relations))
                                nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
 
                        if (atts.Count > 0)
@@ -1413,7 +1570,7 @@ namespace System.Data {
                        elem.Name = XmlConvert.EncodeName (DataSetName);
 
                        // Add namespaces used in DataSet components (tables, columns, ...)
-                       foreach (DataTable dt in Tables) {
+                       foreach (DataTable dt in tables) {
                                foreach (DataColumn col in dt.Columns)
                                        CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
                                CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
@@ -1469,14 +1626,17 @@ namespace System.Data {
                                if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
                                        schema.Namespaces.Add (prefix, ns);
                        }
+
                        return schema;
                }
 
-               private bool CheckExtendedPropertyExists ()
+               private bool CheckExtendedPropertyExists (
+                       DataTableCollection tables,
+                       DataRelationCollection relations)
                {
                        if (ExtendedProperties.Count > 0)
                                return true;
-                       foreach (DataTable dt in Tables) {
+                       foreach (DataTable dt in tables) {
                                if (dt.ExtendedProperties.Count > 0)
                                        return true;
                                foreach (DataColumn col in dt.Columns)
@@ -1486,7 +1646,9 @@ namespace System.Data {
                                        if (c.ExtendedProperties.Count > 0)
                                                return true;
                        }
-                       foreach (DataRelation rel in Relations)
+                       if (relations == null)
+                               return false;
+                       foreach (DataRelation rel in relations)
                                if (rel.ExtendedProperties.Count > 0)
                                        return true;
                        return false;
@@ -1736,6 +1898,7 @@ namespace System.Data {
                                        }
                                        
                                        colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
+                                       xattrs.Clear ();
                                        AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
                                        seq.Items.Add (colElem);
                                }
@@ -1787,6 +1950,7 @@ namespace System.Data {
                                }
 
                                att.UnhandledAttributes = xattrs.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
+                               xattrs.Clear ();
 
                                if (col.MaxLength > -1)
                                        att.SchemaType = GetTableSimpleType (doc, col);