In System.Data.OleDb:
[mono.git] / mcs / class / System.Data / System.Data / XmlDataInferenceLoader.cs
old mode 100755 (executable)
new mode 100644 (file)
index 1324346..b4ee404
@@ -9,12 +9,36 @@
 //\r
 // Design notes are the bottom of the source.\r
 //\r
+
+//
+// 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;\r
 using System.Collections;\r
 using System.Data;\r
 using System.IO; // for Driver\r
 using System.Text; // for Driver\r
 using System.Xml;\r
+using System.Xml.Schema;\r
 using System.Xml.Serialization;\r
 \r
 namespace System.Data\r
@@ -44,6 +68,8 @@ namespace System.Data
 \r
        internal class TableMapping\r
        {\r
+               private bool existsInDataSet;\r
+\r
                public DataTable Table;\r
                public ArrayList Elements = new ArrayList ();\r
                public ArrayList Attributes = new ArrayList ();\r
@@ -57,13 +83,15 @@ namespace System.Data
                // decoded LocalName -> TableMapping\r
                public TableMappingCollection ChildTables = new TableMappingCollection ();\r
 \r
-               public TableMapping (string name)\r
+               public TableMapping (string name, string ns)\r
                {\r
                        Table = new DataTable (name);\r
+                       Table.Namespace = ns;\r
                }\r
 \r
                public TableMapping (DataTable dt)\r
                {\r
+                       existsInDataSet = true;\r
                        Table = dt;\r
                        foreach (DataColumn col in dt.Columns) {\r
                                switch (col.ColumnMapping) {\r
@@ -78,6 +106,11 @@ namespace System.Data
                                        break;\r
                                }\r
                        }\r
+                       PrimaryKey = dt.PrimaryKey.Length > 0 ? dt.PrimaryKey [0] : null;\r
+               }\r
+\r
+               public bool ExistsInDataSet {\r
+                       get { return existsInDataSet; }\r
                }\r
 \r
                public bool ContainsColumn (string name)\r
@@ -85,36 +118,34 @@ namespace System.Data
                        return GetColumn (name) != null;\r
                }\r
 \r
-               public DataColumn GetColumn (string name)
-               {
-                       foreach (DataColumn col in Elements)
-                               if (col.ColumnName == name)
-                                       return col;
-                       foreach (DataColumn col in Attributes)
-                               if (col.ColumnName == name)
-                                       return col;
-                       if (SimpleContent != null && name == SimpleContent.ColumnName)
-                               return SimpleContent;
-                       if (PrimaryKey != null && name == PrimaryKey.ColumnName)
-                               return PrimaryKey;
-                       return null;
-               }
-
-               public void RemoveElementColumn (string name)
-               {
-                       foreach (DataColumn col in Elements) {
-                               if (col.ColumnName == name) {
-                                       Elements.Remove (col);
-                                       return;
-                               }
-                       }
-               }
+               public DataColumn GetColumn (string name)\r
+               {\r
+                       foreach (DataColumn col in Elements)\r
+                               if (col.ColumnName == name)\r
+                                       return col;\r
+                       foreach (DataColumn col in Attributes)\r
+                               if (col.ColumnName == name)\r
+                                       return col;\r
+                       if (SimpleContent != null && name == SimpleContent.ColumnName)\r
+                               return SimpleContent;\r
+                       if (PrimaryKey != null && name == PrimaryKey.ColumnName)\r
+                               return PrimaryKey;\r
+                       return null;\r
+               }\r
+\r
+               public void RemoveElementColumn (string name)\r
+               {\r
+                       foreach (DataColumn col in Elements) {\r
+                               if (col.ColumnName == name) {\r
+                                       Elements.Remove (col);\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
        }\r
 \r
        internal class XmlDataInferenceLoader\r
        {\r
-               const string XmlnsNS = "http://www.w3.org/2000/xmlns/";\r
-\r
                public static void Infer (DataSet dataset, XmlDocument document, XmlReadMode mode, string [] ignoredNamespaces)\r
                {\r
                        new XmlDataInferenceLoader (dataset, document, mode, ignoredNamespaces).ReadXml ();\r
@@ -149,6 +180,9 @@ namespace System.Data
                        // Read one element. It might be DataSet element.\r
                        XmlElement el = document.DocumentElement;\r
 \r
+                       if (el.NamespaceURI == XmlSchema.Namespace)\r
+                               throw new InvalidOperationException ("DataSet is not designed to handle XML Schema as data content. Please use ReadXmlSchema method instead of InferXmlSchema method.");\r
+\r
                        if (IsDocumentElementTable ())\r
                                InferTopLevelTable (el);\r
                        else {\r
@@ -156,9 +190,12 @@ namespace System.Data
                                dataset.DataSetName = localName;\r
                                dataset.Namespace = el.NamespaceURI;\r
                                dataset.Prefix = el.Prefix;\r
-                               foreach (XmlNode n in el.ChildNodes)\r
+                               foreach (XmlNode n in el.ChildNodes) {\r
+                                       if (n.NamespaceURI == XmlSchema.Namespace)\r
+                                               continue;\r
                                        if (n.NodeType == XmlNodeType.Element)\r
                                                InferTopLevelTable (n as XmlElement);\r
+                               }\r
                        }\r
 \r
                        foreach (TableMapping map in tables) {\r
@@ -168,6 +205,8 @@ namespace System.Data
                        }\r
 \r
                        foreach (TableMapping map in tables) {\r
+                               if (map.ExistsInDataSet)\r
+                                       continue;\r
                                if (map.PrimaryKey != null)\r
                                        map.Table.Columns.Add (map.PrimaryKey);\r
                                foreach (DataColumn col in map.Elements)\r
@@ -277,7 +316,7 @@ namespace System.Data
                        string localName = XmlConvert.DecodeName (el.LocalName);\r
                        // FIXME: can be checked later\r
                        CheckExtraneousElementColumn (parentTable, el);\r
-                       TableMapping table = GetMappedTable (parentTable, localName);\r
+                       TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);\r
 \r
                        // If the mapping is actually complex type (not simple\r
                        // repeatable), then ignore it.\r
@@ -300,16 +339,17 @@ namespace System.Data
                        CheckExtraneousElementColumn (parentTable, el);\r
 \r
                        string localName = XmlConvert.DecodeName (el.LocalName);\r
-                       TableMapping table = GetMappedTable (parentTable, localName);\r
+                       TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);\r
 \r
                        bool hasChildElements = false;\r
                        bool hasAttributes = false;\r
                        bool hasText = false;\r
 \r
                        foreach (XmlAttribute attr in el.Attributes) {\r
-                               if (attr.NamespaceURI == XmlnsNS)\r
+                               if (attr.NamespaceURI == XmlConstants.XmlnsNS)\r
                                        continue;\r
-                               if (ignoredNamespaces.Contains (attr.NamespaceURI))\r
+                               if (ignoredNamespaces != null &&\r
+                                       ignoredNamespaces.Contains (attr.NamespaceURI))\r
                                        continue;\r
 \r
                                hasAttributes = true;\r
@@ -333,7 +373,7 @@ namespace System.Data
                                        XmlElement cel = n as XmlElement;\r
                                        string childLocalName = XmlConvert.DecodeName (cel.LocalName);\r
 \r
-                                       switch (GetElementMappingType (cel)) {\r
+                                       switch (GetElementMappingType (cel, ignoredNamespaces, null)) {\r
                                        case ElementMappingType.Simple:\r
                                                InferColumnElement (table, cel);\r
                                                break;\r
@@ -359,17 +399,26 @@ namespace System.Data
                        }\r
                }\r
 \r
-               private TableMapping GetMappedTable (TableMapping parent, string tableName)\r
+               private TableMapping GetMappedTable (TableMapping parent, string tableName, string ns)\r
                {\r
                        TableMapping map = tables [tableName];\r
                        if (map != null) {\r
-                               if (map.ParentTable != parent)\r
-                                       throw new DataException (String.Format ("The table {0} is already allocated as another table's child table.", tableName));\r
+                               if (parent != null && map.ParentTable != null && map.ParentTable != parent)\r
+                                       throw new DataException (String.Format ("The table '{0}' is already allocated as a child of another table '{1}'. Cannot set table '{2}' as parent table.", tableName, map.ParentTable.Table.TableName, parent.Table.TableName));\r
                        } else {\r
-                               map = new TableMapping (tableName);\r
+                               map = new TableMapping (tableName, ns);\r
                                map.ParentTable = parent;\r
                                tables.Add (map);\r
-                               if (parent != null)\r
+                       }\r
+                       if (parent != null) {\r
+                               bool shouldAdd = true;\r
+                               foreach (TableMapping child in parent.ChildTables) {\r
+                                       if (child.Table.TableName == tableName) {\r
+                                               shouldAdd = false;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (shouldAdd)\r
                                        parent.ChildTables.Add (map);\r
                        }\r
                        return map;\r
@@ -407,42 +456,87 @@ namespace System.Data
                        return col;\r
                }\r
 \r
-               private ElementMappingType GetElementMappingType (XmlElement el)\r
+               private static void SetAsExistingTable (XmlElement el, Hashtable existingTables)\r
                {\r
+                       if (existingTables == null)\r
+                               return;\r
+                       ArrayList al = existingTables [el.NamespaceURI] as ArrayList;\r
+                       if (al == null) {\r
+                               al = new ArrayList ();\r
+                               existingTables [el.NamespaceURI] = al;\r
+                       }\r
+                       if (al.Contains (el.LocalName))\r
+                               return;\r
+                       al.Add (el.LocalName);\r
+               }\r
+\r
+               private static ElementMappingType GetElementMappingType (\r
+                       XmlElement el, ArrayList ignoredNamespaces, Hashtable existingTables)\r
+               {\r
+                       if (existingTables != null) {\r
+                               ArrayList al = existingTables [el.NamespaceURI] as ArrayList;\r
+                               if (al != null && al.Contains (el.LocalName))\r
+                                       // this is not precise, but it is enough\r
+                                       // for IsDocumentElementTable().\r
+                                       return ElementMappingType.Complex;\r
+                       }\r
+\r
                        foreach (XmlAttribute attr in el.Attributes) {\r
-                               if (attr.NamespaceURI == XmlnsNS)\r
+                               if (attr.NamespaceURI == XmlConstants.XmlnsNS)\r
                                        continue;\r
-                               if (ignoredNamespaces.Contains (attr.NamespaceURI))\r
+                               if (ignoredNamespaces != null && ignoredNamespaces.Contains (attr.NamespaceURI))\r
                                        continue;\r
+                               SetAsExistingTable (el, existingTables);\r
                                return ElementMappingType.Complex;\r
                        }\r
-                       foreach (XmlNode n in el.ChildNodes)\r
-                               if (n.NodeType == XmlNodeType.Element)\r
+                       foreach (XmlNode n in el.ChildNodes) {\r
+                               if (n.NodeType == XmlNodeType.Element) {\r
+                                       SetAsExistingTable (el, existingTables);\r
                                        return ElementMappingType.Complex;\r
+                               }\r
+                       }\r
 \r
-                       for (XmlNode n = el.NextSibling; n != null; n = n.NextSibling)\r
-                               if (n.NodeType == XmlNodeType.Element && n.LocalName == el.LocalName)\r
-                                       return GetElementMappingType (n as XmlElement) == ElementMappingType.Complex ? ElementMappingType.Complex : ElementMappingType.Repeated;\r
+                       for (XmlNode n = el.NextSibling; n != null; n = n.NextSibling) {\r
+                               if (n.NodeType == XmlNodeType.Element && n.LocalName == el.LocalName) {\r
+                                       SetAsExistingTable (el, existingTables);\r
+                                       return GetElementMappingType (n as XmlElement,\r
+                                               ignoredNamespaces, null)\r
+                                               == ElementMappingType.Complex ?\r
+                                               ElementMappingType.Complex :\r
+                                               ElementMappingType.Repeated;\r
+                               }\r
+                       }\r
 \r
                        return ElementMappingType.Simple;\r
                }\r
 \r
                private bool IsDocumentElementTable ()\r
                {\r
-                       XmlElement top = document.DocumentElement;\r
+                       return IsDocumentElementTable (\r
+                               document.DocumentElement,\r
+                               ignoredNamespaces);\r
+               }\r
+\r
+               internal static bool IsDocumentElementTable (XmlElement top,\r
+                       ArrayList ignoredNamespaces)\r
+               {\r
                        foreach (XmlAttribute attr in top.Attributes) {\r
-                               if (attr.NamespaceURI == XmlnsNS)\r
+                               if (attr.NamespaceURI == XmlConstants.XmlnsNS)\r
                                        continue;\r
-                               if (ignoredNamespaces.Contains (attr.NamespaceURI))\r
+                               if (ignoredNamespaces != null &&\r
+                                       ignoredNamespaces.Contains (attr.NamespaceURI))\r
                                        continue;\r
                                // document element has attributes other than xmlns\r
                                return true;\r
                        }\r
+                       Hashtable existingTables = new Hashtable ();\r
                        foreach (XmlNode n in top.ChildNodes) {\r
                                XmlElement el = n as XmlElement;\r
                                if (el == null)\r
                                        continue;\r
-                               if (this.GetElementMappingType (el) == ElementMappingType.Simple)\r
+                               if (GetElementMappingType (el, ignoredNamespaces,\r
+                                       existingTables)\r
+                                       == ElementMappingType.Simple)\r
                                        return true;\r
                        }\r
                        return false;\r
@@ -454,7 +548,7 @@ namespace System.Data
                private bool IsPossibleColumnElement (XmlElement el)\r
                {\r
                        foreach (XmlAttribute attr in el.Attributes) {\r
-                               if (attr.NamespaceURI == "http://www.w3.org/2000/xmlns/")\r
+                               if (attr.NamespaceURI == XmlConstants.XmlnsNS)\r
                                        continue;\r
                                return false;\r
                        }\r
@@ -467,8 +561,8 @@ namespace System.Data
 }\r
 \r
 \r
-#region FOR_TEST\r
-public class Driver\r
+#if TEST_STANDALONE_INFERENCE\r
+internal class Driver\r
 {\r
        private static void DumpDataTable (DataTable dt)\r
        {\r
@@ -616,7 +710,7 @@ sw = Console.Out;
        }\r
 }\r
 \r
-#endregion\r
+#endif\r
 \r
 //\r
 // * Design Notes\r