Fixes some warnings in System.Data.dll
[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 using System.Xml.Serialization;\r
30 \r
31 \r
32 namespace System.Data\r
33 {\r
34 #if STANDALONE_DRIVER_TEST\r
35         public class Driver\r
36         {\r
37                 public static void Main (string [] args)\r
38                 {\r
39                         if (args.Length == 0) {\r
40                                 Console.WriteLine ("usage: mono xmldatareader.exe filename");\r
41                                 return;\r
42                         }\r
43 \r
44                         Console.WriteLine ("Target file: " + args [0]);\r
45 \r
46                         DataSet ds = new DataSet ();\r
47 //                      ds.InferXmlSchema (args [0], null);\r
48 \r
49                         try {\r
50                                 ds.ReadXml (args [0]);\r
51                         } catch (Exception ex) {\r
52                                 Console.WriteLine ("ReadXml() borked: " + ex.Message);\r
53                                 return;\r
54                         }\r
55                         Console.WriteLine ("---- DataSet ----------------");\r
56                         StringWriter sw = new StringWriter ();\r
57                         PrintDataSet (ds, sw);\r
58                         PrintDataSet (ds, Console.Out);\r
59 \r
60                         ds = new DataSet ();\r
61                         ds.InferXmlSchema (args [0], null);\r
62                         XmlDataReader.ReadXml (ds, new XmlTextReader (args [0]));\r
63                         Console.WriteLine ("---- XmlDataReader ----------------");\r
64                         StringWriter sw2 = new StringWriter ();\r
65                         PrintDataSet (ds, sw2);\r
66 \r
67                         if (sw.ToString () == sw2.ToString ())\r
68                                 Console.WriteLine ("Successful.");\r
69                         else\r
70                                 Console.WriteLine ("Different *************************************************\n" + sw2);\r
71                 }\r
72 \r
73                 private static void PrintDataSet (DataSet ds, TextWriter tw)\r
74                 {\r
75                         tw.WriteLine ("DS::" + ds.DataSetName + ", " + ds.Tables.Count + ", " + ds.Relations.Count);\r
76                         foreach (DataTable dt in ds.Tables)\r
77                                 tw.WriteLine ("DT:" + dt.TableName + ", " + dt.Columns.Count + ", " + dt.Rows.Count);\r
78 \r
79                         ds.WriteXml (tw);\r
80                         tw.WriteLine ();\r
81                 }\r
82         }\r
83 #endif\r
84 \r
85         internal class XmlDataReader\r
86         {\r
87                 const string xmlnsNS = "http://www.w3.org/2000/xmlns/";\r
88 \r
89                 public static void ReadXml (\r
90                         DataSet dataset, XmlReader reader, XmlReadMode mode)\r
91                 {\r
92                         new XmlDataReader (dataset, reader, mode).Process ();\r
93                 }\r
94 \r
95                 DataSet dataset;\r
96                 XmlReader reader;\r
97                 XmlReadMode mode;\r
98 \r
99                 public XmlDataReader (DataSet ds, XmlReader xr, XmlReadMode m)\r
100                 {\r
101                         dataset = ds;\r
102                         reader =xr;\r
103                         mode = m;\r
104                 }\r
105 \r
106                 private void Process ()\r
107                 {\r
108                         bool savedEnforceConstraints =\r
109                                 dataset.EnforceConstraints;\r
110                         try {\r
111                         dataset.EnforceConstraints = false;\r
112                         reader.MoveToContent ();\r
113 \r
114                         if (mode == XmlReadMode.Fragment) {\r
115                                         while (reader.NodeType == XmlNodeType.Element && !reader.EOF) {\r
116                                                 ReadTopLevelElement ();\r
117                                         }\r
118                                 }\r
119                                 else\r
120                                         ReadTopLevelElement ();\r
121                         } finally {\r
122                                 dataset.EnforceConstraints = \r
123                                         savedEnforceConstraints;\r
124                         }\r
125                 }\r
126 \r
127                 private bool IsTopLevelDataSet ()\r
128                 {\r
129                         string local = XmlHelper.Decode (reader.LocalName);\r
130 \r
131                         // No need to check DataSetName. In fact, it is ignored.\r
132 \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                                 (XmlHelper.Decode (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 [XmlHelper.Decode (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 #if NET_2_0\r
198                                         || reader.NamespaceURI == XmlConstants.XmlNS\r
199 #endif\r
200                                                 )\r
201                                                 continue;\r
202                                         ReadElementAttribute (row);\r
203                                 } while (reader.MoveToNextAttribute ());\r
204                                 reader.MoveToElement ();\r
205                         }\r
206 \r
207                         // If not empty element, read content.\r
208                         if (reader.IsEmptyElement) {\r
209                                 reader.Skip ();\r
210                                 reader.MoveToContent ();\r
211                         } else {\r
212                                 int depth = reader.Depth;\r
213                                 reader.Read ();\r
214                                 reader.MoveToContent ();\r
215                                 do {\r
216                                         ReadElementContent (row);\r
217                                 } while (reader.Depth > depth && !reader.EOF);\r
218                                 if (reader.IsEmptyElement)\r
219                                         reader.Read ();\r
220                                 if (reader.NodeType == XmlNodeType.EndElement)\r
221                                         reader.ReadEndElement ();\r
222                                 reader.MoveToContent ();\r
223                         }\r
224                 }\r
225 \r
226                 private void ReadElementAttribute (DataRow row)\r
227                 {\r
228                         DataColumn col = row.Table.Columns [XmlHelper.Decode (reader.LocalName)];\r
229                         if (col == null || col.Namespace != reader.NamespaceURI)\r
230                                 return;\r
231                         row [col] = StringToObject (col.DataType, reader.Value);\r
232                 }\r
233 \r
234                 private void ReadElementContent (DataRow row)\r
235                 {\r
236                         switch (reader.NodeType) {\r
237 \r
238                         case XmlNodeType.EndElement:\r
239                                 // This happens when the content was only whitespace (and skipped by MoveToContent()).\r
240                                 return;\r
241 \r
242                         case XmlNodeType.Element:\r
243                                 ReadElementElement (row);\r
244                                 break;\r
245 \r
246                         case XmlNodeType.Text:\r
247                         case XmlNodeType.CDATA:\r
248                         case XmlNodeType.SignificantWhitespace:\r
249                                 DataColumn simple = null;\r
250                                 DataColumnCollection cols = row.Table.Columns;\r
251                                 for (int i = 0; i < cols.Count; i++) {\r
252                                         DataColumn col = cols [i];\r
253                                         if (col.ColumnMapping ==\r
254                                                 MappingType.SimpleContent) {\r
255                                                 simple = col;\r
256                                                 break;\r
257                                         }\r
258                                 }\r
259                                 string s = reader.ReadString ();\r
260                                 reader.MoveToContent ();\r
261 #if SILLY_MS_COMPATIBLE\r
262 // As to MS, "test string" and "test <!-- comment -->string" are different :P\r
263                                 if (simple != null && row.IsNull (simple))\r
264                                         row [simple] = StringToObject (simple.DataType, s);\r
265 #else\r
266 // But it does not mean we support "123<!-- comment -->456". just allowed for string\r
267                                 if (simple != null)\r
268                                         row [simple] += s;\r
269 #endif\r
270                                 break;\r
271                         case XmlNodeType.Whitespace:\r
272                                 reader.ReadString ();\r
273                                 break;\r
274                         }\r
275                 }\r
276 \r
277                 private void ReadElementElement (DataRow row)\r
278                 {\r
279                         // This child element (for row) might be either simple\r
280                         // content element, or child element\r
281 \r
282                         // MS.NET crashes here... but it seems just a bug.\r
283                         DataColumn col = row.Table.Columns [XmlHelper.Decode (reader.LocalName)];\r
284                         if (col == null ||  col.Namespace != reader.NamespaceURI)\r
285                                 col = null;\r
286 \r
287                         // if col exists, then it should be MappingType.Element\r
288                         if (col != null\r
289                                 && col.ColumnMapping == MappingType.Element) {\r
290 \r
291                                 // TODO: This part is suspicious for\r
292                                 // MS compatibility (test required)\r
293                                 if (col.Namespace != reader.NamespaceURI) {\r
294                                         reader.Skip ();\r
295                                         return;\r
296                                 }\r
297 \r
298                                 bool wasEmpty = reader.IsEmptyElement;\r
299                                 int depth = reader.Depth;\r
300 \r
301                                 if (typeof (IXmlSerializable).IsAssignableFrom (col.DataType)) {\r
302 #if NET_2_0\r
303                                         try {\r
304                                                 // NOTE: ReadElementString works fine with proper XML with CDATA etc,\r
305                                                 // however doesn't behave well with XMLs like the one in \r
306                                                 // https://bugzilla.novell.com/show_bug.cgi?id=377146 which is \r
307                                                 // apparently supported by MS.NET - to maintain compatibility,\r
308                                                 // If the obj implements IXmlSerializable, let obj's ReadXml do the reading\r
309                                                 IXmlSerializable obj = (IXmlSerializable) Activator.CreateInstance (col.DataType, new object [0]);\r
310                                                 if (!reader.IsEmptyElement) {\r
311                                                         obj.ReadXml (reader);\r
312                                                         reader.ReadEndElement ();\r
313                                                 } else {\r
314                                                         reader.Skip ();\r
315                                                 }                                               \r
316                                                 row [col] = obj;\r
317                                         } catch (XmlException) {\r
318 #endif\r
319                                                 // XML is not in accordance to expected standards, try reading the content as an xml doc\r
320                                                 row [col] = reader.ReadInnerXml ();\r
321 #if NET_2_0\r
322                                         } catch (InvalidOperationException) {\r
323 \r
324                                                 row [col] = reader.ReadInnerXml ();\r
325                                         }\r
326 #endif\r
327                                 } else {\r
328                                         row [col] = StringToObject (col.DataType, reader.ReadElementString ());\r
329                                 }\r
330                                         \r
331                                 if (!wasEmpty && reader.Depth > depth) {\r
332                                 // This means, instance does not match with\r
333                                 // the schema (because the instance element\r
334                                 // contains complex content, while specified as\r
335                                 // simple), so just skip to the end of the\r
336                                 // element.\r
337                                         while (reader.Depth > depth)\r
338                                                 reader.Read ();\r
339                                         reader.Read ();\r
340                                 }\r
341                                 reader.MoveToContent ();\r
342                                 return;\r
343                         } else if (col != null) {\r
344                                 // Mismatch column type. Just skip\r
345                                 reader.Skip ();\r
346                                 reader.MoveToContent ();\r
347                                 return;\r
348                         }\r
349 \r
350                         // Otherwise, it might be child table element\r
351                         DataRelationCollection rels = row.Table.ChildRelations;\r
352                         for (int i = 0; i < rels.Count; i++) {\r
353                                 DataRelation rel = rels [i];\r
354                                 if (!rel.Nested)\r
355                                         continue;\r
356                                 DataTable ct = rel.ChildTable;\r
357                                 if (ct.TableName != XmlHelper.Decode (reader.LocalName) || ct.Namespace != reader.NamespaceURI)\r
358                                         continue;\r
359 \r
360                                 DataRow childRow = rel.ChildTable.NewRow ();\r
361                                 ReadElement (childRow);\r
362 \r
363                                 for (int c = 0; c < rel.ChildColumns.Length; c++) {\r
364                                         childRow [rel.ChildColumns [c]]\r
365                                                 = row [rel.ParentColumns [c]];\r
366                                 }\r
367                                 rel.ChildTable.Rows.Add (childRow);\r
368                                 return;\r
369                         }\r
370 \r
371                         // Matched neither of the above: just skip\r
372                         reader.Skip ();\r
373                         reader.MoveToContent ();\r
374                 }\r
375 \r
376                 internal static object StringToObject (Type type, string value)\r
377                 {\r
378                         if (type == null) return value;\r
379 \r
380                         switch (Type.GetTypeCode (type)) {\r
381                                 case TypeCode.Boolean: return XmlConvert.ToBoolean (value);\r
382                                 case TypeCode.Byte: return XmlConvert.ToByte (value);\r
383                                 case TypeCode.Char: return (char)XmlConvert.ToInt32 (value);\r
384 #if NET_2_0\r
385                                 case TypeCode.DateTime: return XmlConvert.ToDateTime (value, XmlDateTimeSerializationMode.Unspecified);\r
386 #else\r
387                                 case TypeCode.DateTime: return XmlConvert.ToDateTime (value);\r
388 #endif\r
389                                 case TypeCode.Decimal: return XmlConvert.ToDecimal (value);\r
390                                 case TypeCode.Double: return XmlConvert.ToDouble (value);\r
391                                 case TypeCode.Int16: return XmlConvert.ToInt16 (value);\r
392                                 case TypeCode.Int32: return XmlConvert.ToInt32 (value);\r
393                                 case TypeCode.Int64: return XmlConvert.ToInt64 (value);\r
394                                 case TypeCode.SByte: return XmlConvert.ToSByte (value);\r
395                                 case TypeCode.Single: return XmlConvert.ToSingle (value);\r
396                                 case TypeCode.UInt16: return XmlConvert.ToUInt16 (value);\r
397                                 case TypeCode.UInt32: return XmlConvert.ToUInt32 (value);\r
398                                 case TypeCode.UInt64: return XmlConvert.ToUInt64 (value);\r
399                         }\r
400 \r
401                         if (type == typeof (TimeSpan)) return XmlConvert.ToTimeSpan (value);\r
402                         if (type == typeof (Guid)) return XmlConvert.ToGuid (value);\r
403                         if (type == typeof (byte[])) return Convert.FromBase64String (value);\r
404                         if (type == typeof (System.Type)) return System.Type.GetType (value);\r
405 \r
406                         return Convert.ChangeType (value, type);\r
407                 }\r
408         }\r
409 }\r