2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Data / System.Data / DataSet.cs
index 8c5bc8b3eb071536e59a41f1a83e428938899884..17815bb815c8470fbe16b9a96b5177be529e6eb6 100644 (file)
 // Copyright (C) Tim Coleman, 2002, 2003
 //
 
+//
+// 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.Collections;
 using System.ComponentModel;
@@ -307,22 +330,34 @@ namespace System.Data {
                                tempTable.AcceptChanges ();
                }
 
+                /// <summary>
+                /// Clears all the tables
+                /// </summary>
                public void Clear ()
                {
                        if (_xmlDataDocument != null)
                                throw new NotSupportedException ("Clear function on dataset and datatable is not supported when XmlDataDocument is bound to the DataSet.");
-                       for (int t = 0; t < tableCollection.Count; t++) {
+                        bool enforceConstraints = this.EnforceConstraints;
+                        this.EnforceConstraints = false;
+                        for (int t = 0; t < tableCollection.Count; t++) {
                                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
@@ -461,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);
                }
@@ -828,105 +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.
-                       bool inferedSchema = false;
-                       switch (mode) {
-                       case XmlReadMode.Auto:
-                               if (Tables.Count > 0)
-                                       goto case XmlReadMode.IgnoreSchema;
-                               else
-                                       goto case XmlReadMode.InferSchema;
-                       case XmlReadMode.InferSchema:
-#if true // sync with the switch immediately below
-                               XmlDocument 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);
-#endif
-                               inferedSchema = true;
-                               break;
-                       case XmlReadMode.IgnoreSchema:
-                       case XmlReadMode.Fragment:
-                               break;
-                       default:
-                               reader.Skip ();
-                               return mode;
+                       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;
+                               }
                        }
-#if true // sync with the switch immediately above
-                       XmlDataReader.ReadXml (this, reader, mode);
-                       if (inferedSchema)
-                               return XmlReadMode.InferSchema;
-                       return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
-#else
-                       XmlDataLoader Loader = new XmlDataLoader (this);
-                       return Loader.LoadData (reader, mode);
-#endif
+
+                       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
 
@@ -938,14 +1089,6 @@ namespace System.Data {
 
                #endregion // Public Events
 
-               #region Destructors
-
-               ~DataSet ()
-               {
-               }
-
-               #endregion Destructors
-
                #region IListSource methods
                IList IListSource.GetList ()
                {
@@ -1008,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)
@@ -1029,10 +1165,10 @@ namespace System.Data {
                        WriteXml (writer, XmlWriteMode.DiffGram);
                }
 
-               XmlSchema IXmlSerializable.GetSchema ()\r
-               {\r
-                       return BuildSchema ();\r
-               }\r
+               XmlSchema IXmlSerializable.GetSchema ()
+               {
+                       return GetSchemaSerializable ();
+               }
 
                protected virtual bool ShouldSerializeRelations ()
                {
@@ -1059,7 +1195,7 @@ namespace System.Data {
                {
                }
 
-               protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
+               internal virtual void OnMergeFailed (MergeFailedEventArgs e)
                {
                        if (MergeFailed != null)
                                MergeFailed (this, e);
@@ -1071,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)
                {
@@ -1117,13 +1305,14 @@ namespace System.Data {
                        //part of another table structure via a nested parent relationship
                        foreach (DataTable table in tableCollection) {
                                bool isTopLevel = true;
+                               /*
                                foreach (DataRelation rel in table.ParentRelations) {
                                        if (rel.Nested) {
                                                isTopLevel = false;
                                                break;
                                        }
                                }
-                               
+                               */
                                if (isTopLevel) {
                                        WriteTable ( writer, table, mode, version);
                                }
@@ -1134,10 +1323,10 @@ namespace System.Data {
                {
                        DataRow[] rows = new DataRow [table.Rows.Count];
                        table.Rows.CopyTo (rows, 0);
-                       WriteTable (writer, rows, mode, version);
+                       WriteTable (writer, rows, mode, version, true);
                }
 
-               private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
+               private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version, bool skipIfNested)
                {
                        //The columns can be attributes, hidden, elements, or simple content
                        //There can be 0-1 simple content cols or 0-* elements
@@ -1150,8 +1339,38 @@ namespace System.Data {
                        SplitColumns (table, out atts, out elements, out simple);
                        //sort out the namespacing
                        string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
+                       int relationCount = table.ParentRelations.Count;
+                       DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
 
                        foreach (DataRow row in rows) {
+                               if (skipIfNested) {
+                                       // Skip rows that is a child of any tables.
+                                       switch (relationCount) {
+                                       case 0:
+                                               break;
+                                       case 1:
+                                               if (!oneRel.Nested)
+                                                       break;
+                                               if (row.GetParentRow (oneRel) != null)
+                                                       continue;
+                                               break;
+                                       case 2:
+                                               bool skip = false;
+                                               for (int i = 0; i < table.ParentRelations.Count; i++) {
+                                                       DataRelation prel = table.ParentRelations [i];
+                                                       if (!prel.Nested)
+                                                               continue;
+                                                       if (row.GetParentRow (prel) != null) {
+                                                               skip = true;
+                                                               continue;
+                                                       }
+                                               }
+                                               if (skip)
+                                                       continue;
+                                               break;
+                                       }
+                               }
+
                                if (!row.HasVersion(version) || 
                                   (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
                                      && version == DataRowVersion.Original))
@@ -1169,7 +1388,7 @@ namespace System.Data {
 
                                // If all of the columns were null, we have to write empty element
                                if (AllNulls) {
-                                       writer.WriteElementString (table.TableName, "");
+                                       writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
                                        continue;
                                }
                                
@@ -1190,7 +1409,7 @@ namespace System.Data {
                                
                                foreach (DataRelation relation in table.ChildRelations) {
                                        if (relation.Nested) {
-                                               WriteTable (writer, row.GetChildRows (relation), mode, version);
+                                               WriteTable (writer, row.GetChildRows (relation), mode, version, false);
                                        }
                                }
                                
@@ -1211,14 +1430,15 @@ namespace System.Data {
                                colnspc = col.Namespace;
        
                        //TODO check if I can get away with write element string
-                       WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
+                       WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
                        writer.WriteString (WriteObjectXml (rowObject));
                        writer.WriteEndElement ();
                }
 
                private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
                {
-                       WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
+                       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)
@@ -1226,11 +1446,11 @@ namespace System.Data {
                        //sort out the namespacing
                        string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
 
-                       WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
+                       WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
 
                        if (mode == XmlWriteMode.DiffGram) {
                                WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
-                               WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
+                               WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
                                string modeName = null;
                                if (row.RowState == DataRowState.Modified)
                                        modeName = "modified";
@@ -1287,13 +1507,14 @@ namespace System.Data {
                        }
                        writer.WriteEndElement (); // DataSet name or diffgr:diffgram
                }
+               
 
                private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
                {
                        if (ns == String.Empty)
                                return;
                        if (ns != nsmgr.DefaultNamespace) {
-                               if (nsmgr.LookupNamespace (prefix) != ns) {
+                               if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
                                        for (int i = 1; i < int.MaxValue; i++) {
                                                string p = nsmgr.NameTable.Add ("app" + i);
                                                if (!nsmgr.HasNamespace (p)) {
@@ -1325,18 +1546,22 @@ 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 (tables, relations))
+                               nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
 
                        if (atts.Count > 0)
                                schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
@@ -1345,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);
@@ -1363,6 +1588,8 @@ namespace System.Data {
 
                        elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
 
+                       AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
+
                        XmlSchemaComplexType complex = new XmlSchemaComplexType ();
                        elem.SchemaType = complex;
 
@@ -1393,38 +1620,62 @@ namespace System.Data {
 
                        schema.Items.Add (elem);
                        
-                       AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
+                       AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
                        foreach (string prefix in nsmgr) {
-                               string ns = nsmgr.LookupNamespace (prefix);
+                               string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
                                if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
                                        schema.Namespaces.Add (prefix, ns);
                        }
+
                        return schema;
                }
-               
+
+               private bool CheckExtendedPropertyExists (
+                       DataTableCollection tables,
+                       DataRelationCollection relations)
+               {
+                       if (ExtendedProperties.Count > 0)
+                               return true;
+                       foreach (DataTable dt in tables) {
+                               if (dt.ExtendedProperties.Count > 0)
+                                       return true;
+                               foreach (DataColumn col in dt.Columns)
+                                       if (col.ExtendedProperties.Count > 0)
+                                               return true;
+                               foreach (Constraint c in dt.Constraints)
+                                       if (c.ExtendedProperties.Count > 0)
+                                               return true;
+                       }
+                       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) 
+               private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc
                {
                        // first add all unique constraints.
-                       Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
+                       Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
                        // Add all foriegn key constraints.
-                       AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
+                       AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
                }
                
                // Add unique constaraints to the schema.
                // return hashtable with the names of all XmlSchemaUnique elements we created.
-               private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
+               private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
                {
-                       XmlDocument doc = new XmlDocument();
                        Hashtable uniqueNames = new Hashtable();
                        foreach (DataTable table in tables) {
                                
-                               foreach (Constraint constaint in table.Constraints) {
+                               foreach (Constraint constraint in table.Constraints) {
                                        
-                                       if (constaint is UniqueConstraint) {
+                                       if (constraint is UniqueConstraint) {
                                                ArrayList attrs = new ArrayList ();
                                                XmlAttribute attrib;
-                                               UniqueConstraint uqConst = (UniqueConstraint)constaint;
+                                               UniqueConstraint uqConst = (UniqueConstraint) constraint;
                                                XmlSchemaUnique uniq = new XmlSchemaUnique ();
                                                
                                                // if column of the constraint is hidden do not write the constraint.
@@ -1463,10 +1714,13 @@ namespace System.Data {
                                                XmlSchemaXPath field;
                                                foreach (DataColumn column in uqConst.Columns) {
                                                        field = new XmlSchemaXPath();
-                                                       field.XPath = constraintPrefix+column.ColumnName;
+                                                       string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
+                                                       field.XPath = typePrefix + constraintPrefix+column.ColumnName;
                                                        uniq.Fields.Add(field);
                                                }
                                
+                                               AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
+
                                                elem.Constraints.Add (uniq);
                                                uniqueNames.Add (uniq.Name, null);
                                        }
@@ -1476,11 +1730,10 @@ namespace System.Data {
                }
                
                // Add the foriegn keys to the schema.
-               private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
+               private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
                {
                        if (relations == null) return;
                        
-                       XmlDocument doc = new XmlDocument();
                        foreach (DataRelation rel in relations) {
                                
                                if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
@@ -1529,10 +1782,14 @@ namespace System.Data {
                                XmlSchemaXPath field;
                                foreach (DataColumn column in rel.ChildColumns) {
                                        field = new XmlSchemaXPath();
-                                       field.XPath = constraintPrefix+column.ColumnName;
+                                       string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
+                                       field.XPath = typePrefix + constraintPrefix + column.ColumnName;
                                        keyRef.Fields.Add(field);
                                }
+
                                keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
+                               AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
+
                                elem.Constraints.Add (keyRef);
                        }
                }
@@ -1542,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 ();
@@ -1553,7 +1813,6 @@ namespace System.Data {
 
                        XmlSchemaObjectCollection schemaAttributes = null;
 
-                       //TODO - what about the simple content?
                        if (simple != null) {
                                // add simpleContent
                                XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
@@ -1566,7 +1825,7 @@ namespace System.Data {
                                
                                // add ordinal attribute
                                xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
-                               xlmAttrs[1].Value = simple.Ordinal.ToString();
+                               xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
                                simpleContent.UnhandledAttributes = xlmAttrs;
                                
                                
@@ -1585,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) {
@@ -1603,13 +1860,19 @@ namespace System.Data {
 
                                        if (col.AutoIncrementSeed != 0) {
                                                xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
-                                               xattr.Value = col.AutoIncrementSeed.ToString();
+                                               xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
                                                xattrs.Add (xattr);
                                        }
 
                                        if (col.DefaultValue.ToString () != String.Empty)
-                                               colElem.DefaultValue = col.DefaultValue.ToString ();
-                                       
+                                               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);
                                        
@@ -1635,10 +1898,10 @@ namespace System.Data {
                                        }
                                        
                                        colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
+                                       xattrs.Clear ();
+                                       AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
                                        seq.Items.Add (colElem);
                                }
-                               if (seq.Items.Count > 0)
-                                       complex.Particle = seq;
 
                                foreach (DataRelation rel in table.ChildRelations) {
                                        if (rel.Nested) {
@@ -1647,6 +1910,8 @@ namespace System.Data {
                                                        el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
                                                } else {
                                                        XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
+                                                       el.MinOccurs = 0;
+                                                       el.MaxOccursString = "unbounded";
                                                        XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
                                                        ct.Name = el.Name;
                                                        el.SchemaType = null;
@@ -1656,6 +1921,9 @@ namespace System.Data {
                                                }
                                        }
                                }
+
+                               if (seq.Items.Count > 0)
+                                       complex.Particle = seq;
                        }
 
                        //Then a list of attributes
@@ -1670,13 +1938,51 @@ 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);
                        }
 
+                       AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
+
                        return elem;
                }
 
+               private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
+               {
+                       ArrayList attList = new ArrayList ();
+                       XmlAttribute xmlAttr;
+
+                       if (xsobj.UnhandledAttributes != null)
+                               attList.AddRange (xsobj.UnhandledAttributes);
+
+                       // add extended properties to xs:element
+                       foreach (DictionaryEntry de in props) {
+                               xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
+                               xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
+                               attList.Add (xmlAttr);
+                       }
+                       if (attList.Count > 0)
+                               xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
+               }
+
                private string SafeNS (string ns)
                {
                        return ns != null ? ns : String.Empty;