2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Data / System.Data / DataSet.cs
index f02c70ca50c0814c318fafb14de2eb57af3003b0..17815bb815c8470fbe16b9a96b5177be529e6eb6 100644 (file)
@@ -340,18 +340,24 @@ namespace System.Data {
                         bool enforceConstraints = this.EnforceConstraints;
                         this.EnforceConstraints = false;
                         for (int t = 0; t < tableCollection.Count; t++) {
-                               tableCollection [t].Clear ();
+                               tableCollection[t].Clear ();
                        }
                         this.EnforceConstraints = enforceConstraints;
                }
 
                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)
                {
@@ -1282,7 +1437,8 @@ namespace System.Data {
 
                private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
                {
-                       WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
+                       if (!row.IsNull (col))
+                               WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
                }
 
                private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
@@ -1390,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)
@@ -1412,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);
@@ -1468,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)
@@ -1485,14 +1646,16 @@ 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;
                }
 
                // Add all constraints in all tables to the schema.
-               private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc)
+               private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc) 
                {
                        // first add all unique constraints.
                        Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
@@ -1552,7 +1715,7 @@ namespace System.Data {
                                                foreach (DataColumn column in uqConst.Columns) {
                                                        field = new XmlSchemaXPath();
                                                        string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
-                                                       field.XPath = typePrefix + constraintPrefix + column.ColumnName;
+                                                       field.XPath = typePrefix + constraintPrefix+column.ColumnName;
                                                        uniq.Fields.Add(field);
                                                }
                                
@@ -1636,7 +1799,10 @@ namespace System.Data {
                        ArrayList elements;
                        ArrayList atts;
                        DataColumn simple;
-                       
+
+                       ArrayList xattrs = new ArrayList();
+                       XmlAttribute xattr;
+
                        SplitColumns (table, out atts, out elements, out simple);
 
                        XmlSchemaElement elem = new XmlSchemaElement ();
@@ -1678,8 +1844,6 @@ namespace System.Data {
                                        
                                        // Add element for the column.
                                        XmlSchemaElement colElem = new XmlSchemaElement ();
-                                       ArrayList xattrs = new ArrayList();
-                                       XmlAttribute xattr;
                                        colElem.Name = col.ColumnName;
                                
                                        if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
@@ -1702,7 +1866,13 @@ namespace System.Data {
 
                                        if (col.DefaultValue.ToString () != String.Empty)
                                                colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
-                                       
+
+                                       if (col.ReadOnly) {
+                                               xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
+                                               xattr.Value = "true";
+                                               xattrs.Add (xattr);
+                                       }
+
                                        if (col.MaxLength < 0)
                                                colElem.SchemaTypeName = MapType (col.DataType);
                                        
@@ -1728,6 +1898,7 @@ namespace System.Data {
                                        }
                                        
                                        colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
+                                       xattrs.Clear ();
                                        AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
                                        seq.Items.Add (colElem);
                                }
@@ -1767,7 +1938,24 @@ namespace System.Data {
                                        // FIXME: Handle prefix mapping correctly.
                                        schemaToAdd.Namespaces.Add (prefix, col.Namespace);
                                }
-                               att.SchemaTypeName = MapType (col.DataType);
+                               if (!col.AllowDBNull)
+                                       att.Use = XmlSchemaUse.Required;
+                               if (col.DefaultValue.ToString () != String.Empty)
+                                       att.DefaultValue = WriteObjectXml (col.DefaultValue);
+
+                               if (col.ReadOnly) {
+                                       xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
+                                       xattr.Value = "true";
+                                       xattrs.Add (xattr);
+                               }
+
+                               att.UnhandledAttributes = xattrs.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
+                               xattrs.Clear ();
+
+                               if (col.MaxLength > -1)
+                                       att.SchemaType = GetTableSimpleType (doc, col);
+                               else
+                                       att.SchemaTypeName = MapType (col.DataType);
                                // FIXME: what happens if extended properties are set on attribute columns??
                                schemaAttributes.Add (att);
                        }
@@ -1793,7 +1981,7 @@ namespace System.Data {
                        }
                        if (attList.Count > 0)
                                xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
-               }
+               }
 
                private string SafeNS (string ns)
                {