Drop of Mainsoft.System.Data
[mono.git] / mcs / class / System.Data / System.Data / XmlDataReader.cs
1 \r
2 //\r
3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
4 //\r
5 // Permission is hereby granted, free of charge, to any person obtaining\r
6 // a copy of this software and associated documentation files (the\r
7 // "Software"), to deal in the Software without restriction, including\r
8 // without limitation the rights to use, copy, modify, merge, publish,\r
9 // distribute, sublicense, and/or sell copies of the Software, and to\r
10 // permit persons to whom the Software is furnished to do so, subject to\r
11 // the following conditions:\r
12 // \r
13 // The above copyright notice and this permission notice shall be\r
14 // included in all copies or substantial portions of the Software.\r
15 // \r
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
23 //\r
24 \r
25 using System;\r
26 using System.IO;\r
27 using System.Data;\r
28 using System.Xml;\r
29 \r
30 \r
31 namespace System.Data\r
32 {\r
33 #if STANDALONE_DRIVER_TEST\r
34         public class Driver\r
35         {\r
36                 public static void Main (string [] args)\r
37                 {\r
38                         if (args.Length == 0) {\r
39                                 Console.WriteLine ("usage: mono xmldatareader.exe filename");\r
40                                 return;\r
41                         }\r
42 \r
43                         Console.WriteLine ("Target file: " + args [0]);\r
44 \r
45                         DataSet ds = new DataSet ();\r
46 //                      ds.InferXmlSchema (args [0], null);\r
47 \r
48                         try {\r
49                                 ds.ReadXml (args [0]);\r
50                         } catch (Exception ex) {\r
51                                 Console.WriteLine ("ReadXml() borked: " + ex.Message);\r
52                                 return;\r
53                         }\r
54                         Console.WriteLine ("---- DataSet ----------------");\r
55                         StringWriter sw = new StringWriter ();\r
56                         PrintDataSet (ds, sw);\r
57                         PrintDataSet (ds, Console.Out);\r
58 \r
59                         ds = new DataSet ();\r
60                         ds.InferXmlSchema (args [0], null);\r
61                         XmlDataReader.ReadXml (ds, new XmlTextReader (args [0]));\r
62                         Console.WriteLine ("---- XmlDataReader ----------------");\r
63                         StringWriter sw2 = new StringWriter ();\r
64                         PrintDataSet (ds, sw2);\r
65 \r
66                         if (sw.ToString () == sw2.ToString ())\r
67                                 Console.WriteLine ("Successful.");\r
68                         else\r
69                                 Console.WriteLine ("Different *************************************************\n" + sw2);\r
70                 }\r
71 \r
72                 private static void PrintDataSet (DataSet ds, TextWriter tw)\r
73                 {\r
74                         tw.WriteLine ("DS::" + ds.DataSetName + ", " + ds.Tables.Count + ", " + ds.Relations.Count);\r
75                         foreach (DataTable dt in ds.Tables)\r
76                                 tw.WriteLine ("DT:" + dt.TableName + ", " + dt.Columns.Count + ", " + dt.Rows.Count);\r
77 \r
78                         ds.WriteXml (tw);\r
79                         tw.WriteLine ();\r
80                 }\r
81         }\r
82 #endif\r
83 \r
84         internal class XmlDataReader\r
85         {\r
86                 const string xmlnsNS = "http://www.w3.org/2000/xmlns/";\r
87 \r
88                 public static void ReadXml (\r
89                         DataSet dataset, XmlReader reader, XmlReadMode mode)\r
90                 {\r
91                         new XmlDataReader (dataset, reader, mode).Process ();\r
92                 }\r
93 \r
94                 DataSet dataset;\r
95                 XmlReader reader;\r
96                 XmlReadMode mode;\r
97 \r
98                 public XmlDataReader (DataSet ds, XmlReader xr, XmlReadMode m)\r
99                 {\r
100                         dataset = ds;\r
101                         reader =xr;\r
102                         mode = m;\r
103                 }\r
104 \r
105                 private void Process ()\r
106                 {\r
107                         bool savedEnforceConstraints =\r
108                                 dataset.EnforceConstraints;\r
109                         try {\r
110                         dataset.EnforceConstraints = false;\r
111                         reader.MoveToContent ();\r
112 \r
113                         if (mode == XmlReadMode.Fragment) {\r
114                                         while (reader.NodeType == XmlNodeType.Element && !reader.EOF) {\r
115                                                 ReadTopLevelElement ();\r
116                                         }\r
117                                 }\r
118                                 else\r
119                                         ReadTopLevelElement ();\r
120                         } finally {\r
121                                 dataset.EnforceConstraints = \r
122                                         savedEnforceConstraints;\r
123                         }\r
124                 }\r
125 \r
126                 private bool IsTopLevelDataSet ()\r
127                 {\r
128                         string local = XmlConvert.DecodeName (reader.LocalName);\r
129 \r
130                         // No need to check DataSetName. In fact, it is ignored.\r
131 \r
132                         bool ambiguous = false;\r
133                         DataTable dt = dataset.Tables [local];\r
134                         if (dt == null)\r
135                                 return true;\r
136 \r
137                         XmlDocument doc = new XmlDocument ();\r
138                         XmlElement el = (XmlElement) doc.ReadNode (reader);\r
139                         doc.AppendChild (el);\r
140                         reader = new XmlNodeReader (el);\r
141                         reader.MoveToContent ();\r
142 \r
143                         return !XmlDataInferenceLoader.IsDocumentElementTable (\r
144                                 el, null);\r
145                 }\r
146 \r
147                 private void ReadTopLevelElement ()\r
148                 {\r
149                         if (mode == XmlReadMode.Fragment &&\r
150                                 (XmlConvert.DecodeName (reader.LocalName) !=\r
151                                 dataset.DataSetName ||\r
152                                 reader.NamespaceURI != dataset.Namespace))\r
153                                 reader.Skip ();\r
154                         else if (mode == XmlReadMode.Fragment ||\r
155                                 IsTopLevelDataSet ()) {\r
156                         int depth = reader.Depth;\r
157                         reader.Read ();\r
158                         reader.MoveToContent ();\r
159                         do {\r
160                                 ReadDataSetContent ();\r
161                         } while (reader.Depth > depth && !reader.EOF);\r
162                         if (reader.NodeType == XmlNodeType.EndElement)\r
163                                 reader.ReadEndElement ();\r
164                         reader.MoveToContent ();\r
165                 }\r
166                         else\r
167                                 ReadDataSetContent ();\r
168                 }\r
169 \r
170                 private void ReadDataSetContent ()\r
171                 {\r
172                         DataTable table = dataset.Tables [XmlConvert.DecodeName (reader.LocalName)];\r
173                         if (table == null || table.Namespace != reader.NamespaceURI) {\r
174                                 reader.Skip ();\r
175                                 reader.MoveToContent ();\r
176                                 return; // skip if there is no matching table\r
177                         }\r
178 \r
179                         // skip if namespace does not match.\r
180                         if (table.Namespace != reader.NamespaceURI) {\r
181                                 reader.Skip ();\r
182                                 reader.MoveToContent ();\r
183                                 return; // skip if there is no matching table\r
184                         }\r
185 \r
186                         DataRow row = table.NewRow ();\r
187                         ReadElement (row);\r
188                         table.Rows.Add (row);\r
189                 }\r
190 \r
191                 private void ReadElement (DataRow row)\r
192                 {\r
193                         // Consume attributes\r
194                         if (reader.MoveToFirstAttribute ()) {\r
195                                 do {\r
196                                         if (reader.NamespaceURI == XmlConstants.XmlnsNS)\r
197                                                 continue;\r
198                                         ReadElementAttribute (row);\r
199                                 } while (reader.MoveToNextAttribute ());\r
200                                 reader.MoveToElement ();\r
201                         }\r
202 \r
203                         // If not empty element, read content.\r
204                         if (reader.IsEmptyElement) {\r
205                                 reader.Skip ();\r
206                                 reader.MoveToContent ();\r
207                         } else {\r
208                                 int depth = reader.Depth;\r
209                                 reader.Read ();\r
210                                 reader.MoveToContent ();\r
211                                 do {\r
212                                         ReadElementContent (row);\r
213                                 } while (reader.Depth > depth && !reader.EOF);\r
214                                 if (reader.IsEmptyElement)\r
215                                         reader.Read ();\r
216                                 if (reader.NodeType == XmlNodeType.EndElement)\r
217                                         reader.ReadEndElement ();\r
218                                 reader.MoveToContent ();\r
219                         }\r
220                 }\r
221 \r
222                 private void ReadElementAttribute (DataRow row)\r
223                 {\r
224                         DataColumn col = row.Table.Columns [XmlConvert.DecodeName (reader.LocalName)];\r
225                         if (col == null || col.Namespace != reader.NamespaceURI)\r
226                                 return;\r
227                         row [col] = StringToObject (col.DataType, reader.Value);\r
228                 }\r
229 \r
230                 private void ReadElementContent (DataRow row)\r
231                 {\r
232                         switch (reader.NodeType) {\r
233 \r
234                         case XmlNodeType.EndElement:\r
235                                 // This happens when the content was only whitespace (and skipped by MoveToContent()).\r
236                                 return;\r
237 \r
238                         case XmlNodeType.Element:\r
239                                 ReadElementElement (row);\r
240                                 break;\r
241 \r
242                         case XmlNodeType.Text:\r
243                         case XmlNodeType.CDATA:\r
244                         case XmlNodeType.SignificantWhitespace:\r
245                                 DataColumn simple = null;\r
246                                 DataColumnCollection cols = row.Table.Columns;\r
247                                 for (int i = 0; i < cols.Count; i++) {\r
248                                         DataColumn col = cols [i];\r
249                                         if (col.ColumnMapping ==\r
250                                                 MappingType.SimpleContent) {\r
251                                                 simple = col;\r
252                                                 break;\r
253                                         }\r
254                                 }\r
255                                 string s = reader.ReadString ();\r
256                                 reader.MoveToContent ();\r
257 #if SILLY_MS_COMPATIBLE\r
258 // As to MS, "test string" and "test <!-- comment -->string" are different :P\r
259                                 if (simple != null && row.IsNull (simple))\r
260                                         row [simple] = StringToObject (simple.DataType, s);\r
261 #else\r
262 // But it does not mean we support "123<!-- comment -->456". just allowed for string\r
263                                 if (simple != null)\r
264                                         row [simple] += s;\r
265 #endif\r
266                                 break;\r
267                         case XmlNodeType.Whitespace:\r
268                                 reader.ReadString ();\r
269                                 break;\r
270                         }\r
271                 }\r
272 \r
273                 private void ReadElementElement (DataRow row)\r
274                 {\r
275                         // This child element (for row) might be either simple\r
276                         // content element, or child element\r
277 \r
278                         // MS.NET crashes here... but it seems just a bug.\r
279 //                      DataColumn col = row.Table.Columns [XmlConvert.DecodeName (reader.LocalName)];\r
280                         DataColumn col = null;\r
281                         DataColumnCollection cols = row.Table.Columns;\r
282                         for (int i = 0; i < cols.Count; i++) {\r
283                                 if (cols [i].ColumnName == XmlConvert.DecodeName (reader.LocalName) && cols [i].Namespace == reader.NamespaceURI) {\r
284                                         col = cols [i];\r
285                                         break;\r
286                                 }\r
287                         }\r
288 \r
289 \r
290                         // if col exists, then it should be MappingType.Element\r
291                         if (col != null\r
292                                 && col.ColumnMapping == MappingType.Element) {\r
293 \r
294                                 // TODO: This part is suspicious for\r
295                                 // MS compatibility (test required)\r
296                                 if (col.Namespace != reader.NamespaceURI) {\r
297                                         reader.Skip ();\r
298                                         return;\r
299                                 }\r
300 \r
301                                 bool wasEmpty = reader.IsEmptyElement;\r
302                                 int depth = reader.Depth;\r
303                                 row [col] = StringToObject (col.DataType, reader.ReadElementString ());\r
304                                 if (!wasEmpty && reader.Depth > depth) {\r
305                                 // This means, instance does not match with\r
306                                 // the schema (because the instance element\r
307                                 // contains complex content, while specified as\r
308                                 // simple), so just skip to the end of the\r
309                                 // element.\r
310                                         while (reader.Depth > depth)\r
311                                                 reader.Read ();\r
312                                         reader.Read ();\r
313                                 }\r
314                                 reader.MoveToContent ();\r
315                                 return;\r
316                         } else if (col != null) {\r
317                                 // Mismatch column type. Just skip\r
318                                 reader.Skip ();\r
319                                 reader.MoveToContent ();\r
320                                 return;\r
321                         }\r
322 \r
323                         // Otherwise, it might be child table element\r
324                         DataRelationCollection rels = row.Table.ChildRelations;\r
325                         for (int i = 0; i < rels.Count; i++) {\r
326                                 DataRelation rel = rels [i];\r
327                                 if (!rel.Nested)\r
328                                         continue;\r
329                                 DataTable ct = rel.ChildTable;\r
330                                 if (ct.TableName != XmlConvert.DecodeName (reader.LocalName) || ct.Namespace != reader.NamespaceURI)\r
331                                         continue;\r
332 \r
333                                 DataRow childRow = rel.ChildTable.NewRow ();\r
334                                 ReadElement (childRow);\r
335 \r
336                                 for (int c = 0; c < rel.ChildColumns.Length; c++) {\r
337                                         childRow [rel.ChildColumns [c]]\r
338                                                 = row [rel.ParentColumns [c]];\r
339                                 }\r
340                                 rel.ChildTable.Rows.Add (childRow);\r
341                                 return;\r
342                         }\r
343 \r
344                         // Matched neither of the above: just skip\r
345                         reader.Skip ();\r
346                         reader.MoveToContent ();\r
347                 }\r
348 \r
349                 internal static object StringToObject (Type type, string value)\r
350                 {\r
351                         if (type == null) return value;\r
352 \r
353                         switch (Type.GetTypeCode (type)) {\r
354                                 case TypeCode.Boolean: return XmlConvert.ToBoolean (value);\r
355                                 case TypeCode.Byte: return XmlConvert.ToByte (value);\r
356                                 case TypeCode.Char: return (char)XmlConvert.ToInt32 (value);\r
357                                 case TypeCode.DateTime: return XmlConvert.ToDateTime (value);\r
358                                 case TypeCode.Decimal: return XmlConvert.ToDecimal (value);\r
359                                 case TypeCode.Double: return XmlConvert.ToDouble (value);\r
360                                 case TypeCode.Int16: return XmlConvert.ToInt16 (value);\r
361                                 case TypeCode.Int32: return XmlConvert.ToInt32 (value);\r
362                                 case TypeCode.Int64: return XmlConvert.ToInt64 (value);\r
363                                 case TypeCode.SByte: return XmlConvert.ToSByte (value);\r
364                                 case TypeCode.Single: return XmlConvert.ToSingle (value);\r
365                                 case TypeCode.UInt16: return XmlConvert.ToUInt16 (value);\r
366                                 case TypeCode.UInt32: return XmlConvert.ToUInt32 (value);\r
367                                 case TypeCode.UInt64: return XmlConvert.ToUInt64 (value);\r
368                         }\r
369 \r
370                         if (type == typeof (TimeSpan)) return XmlConvert.ToTimeSpan (value);\r
371                         if (type == typeof (Guid)) return XmlConvert.ToGuid (value);\r
372                         if (type == typeof (byte[])) return Convert.FromBase64String (value);\r
373 \r
374                         return Convert.ChangeType (value, type);\r
375                 }\r
376         }\r
377 }\r