Check for empty elements to avoid NullReferenceException.
[mono.git] / mcs / class / System.Data / System.Data / XmlSchemaMapper.cs
1 //
2 // mcs/class/System.Data/System.Data/XmlSchemaMapper.cs
3 //
4 // Purpose: Maps XmlSchema to DataSet 
5 //
6 // class: XmlSchemaMapper
7 // assembly: System.Data.dll
8 // namespace: System.Data
9 //
10 // Author:
11 //     Ville Palo <vi64pa@koti.soon.fi>
12 //
13 // (C) 2002 Ville Palo
14 //
15 //
16
17 using System;
18 using System.Data;
19 using System.Xml;
20 using System.Xml.Schema;
21 using System.Collections;
22 using System.Globalization;
23
24 namespace System.Data {
25
26         internal class XmlSchemaMapper
27         {       
28                 #region Fields
29
30                 private DataSet DSet;
31                 enum ElementType {ELEMENT_UNDEFINED, ELEMENT_TABLE, ELEMENT_COLUMN};
32                 private Hashtable TypeCollection = new Hashtable ();
33                 private Hashtable ElementCollection = new Hashtable ();
34
35                 #endregion // Fields
36
37                 #region Constructors
38
39                 public XmlSchemaMapper (DataSet dataset)
40                 {
41                         DSet = dataset;
42                 }
43
44                 #endregion // Constructors
45
46                 #region Public methods
47
48                 public void Read (XmlReader Reader)
49                 {
50                         XmlSchema Schema = XmlSchema.Read (Reader, new ValidationEventHandler (OnXmlSchemaValidation));
51                         
52                         // read items
53                         foreach (XmlSchemaObject Item in Schema.Items)
54                                 ReadXmlSchemaItem (Item);
55                 }
56
57                 #endregion // Public methods
58
59                 #region Private methods
60
61                 private void ReadXmlSchemaItem (XmlSchemaObject Item)
62                 {
63                         XmlSchemaObject SchemaObject;
64                         
65                         if (Item is XmlSchemaType)
66                                 ReadXmlSchemaType ((XmlSchemaType)Item);
67                         else if (Item is XmlSchemaElement)
68                                 ReadXmlSchemaElement (Item as XmlSchemaElement, ElementType.ELEMENT_UNDEFINED);
69                 }
70
71                 private void ReadXmlSchemaSequence (XmlSchemaSequence Sequence)
72                 {
73                         ReadXmlSchemaSequence (Sequence, null);
74                 }
75
76                 private void ReadXmlSchemaSequence (XmlSchemaSequence Sequence, DataTable Table)
77                 {
78                         foreach (XmlSchemaObject TempObj in Sequence.Items) {
79                                 if (TempObj is XmlSchemaElement){
80                                         XmlSchemaElement schemaElement = (XmlSchemaElement)TempObj;
81                                         // the element can be a Column or a Table
82                                         // tables do not have a type.
83                                         if (schemaElement.SchemaTypeName.Name.Length == 0) 
84                                                 ReadXmlSchemaElement (schemaElement, ElementType.ELEMENT_TABLE, Table);
85                                         else
86                                                 ReadXmlSchemaElement (schemaElement, ElementType.ELEMENT_COLUMN, Table);
87                                 }
88                         }
89                 }
90
91                 private void ReadXmlSchemaChoice (XmlSchemaChoice Choice)
92                 {
93                         XmlSchemaObject SchemaObject;
94                         foreach (XmlSchemaObject TempObject in Choice.Items) {
95                                 if ((SchemaObject = TempObject as XmlSchemaElement) != null)
96                                         ReadXmlSchemaElement ((XmlSchemaElement)SchemaObject, ElementType.ELEMENT_TABLE);
97                         }
98                 }
99
100                 private void ReadXmlSchemaElement (XmlSchemaElement Element)
101                 {
102                         ReadXmlSchemaElement (Element, ElementType.ELEMENT_UNDEFINED);
103                 }
104
105                 private void ReadXmlSchemaElement (XmlSchemaElement Element, ElementType ElType)
106                 {
107                         ReadXmlSchemaElement (Element, ElType, null);
108                 }
109
110                 private void ReadXmlSchemaElement (XmlSchemaElement Element, ElementType ElType, DataTable Table)
111                 {
112                         Hashtable Attributes = ReadUnhandledAttributes (Element.UnhandledAttributes);
113                         DataTable Table2 = null;
114
115                         if (Attributes.Contains ("IsDataSet")) { // DataSet -elemt
116                                 if (String.Compare (Attributes ["IsDataSet"].ToString (), "true", true) == 0)
117                                         DSet.DataSetName = Element.Name;
118                         }
119                         else if (Element.SchemaTypeName != null && Element.SchemaTypeName.Namespace != XmlConstants.SchemaNamespace 
120                                  && Element.SchemaTypeName.Name != String.Empty) {
121
122                                 //
123                                 // If type is not standard type
124                                 //
125
126                                 DataTable TempTable = new DataTable (Element.Name);
127                                 DSet.Tables.Add (TempTable);
128                                 
129                                 // If type is already defined in schema read it...                              
130                                 if (TypeCollection.Contains (Element.SchemaTypeName.ToString ()))
131                                         ReadXmlSchemaType ((XmlSchemaType)TypeCollection [Element.SchemaTypeName.ToString ()], TempTable);
132                                 else // but if it's not yet defined put it safe to wait if we need it later. 
133                                         ElementCollection.Add (Element.SchemaTypeName.Name, TempTable);
134
135                         }
136                         else if (Element.RefName != null && Element.RefName.Name != string.Empty) { // if there is a ref=
137
138                                 if (ElementCollection.Contains (Element.RefName.Name))
139                                         ReadXmlSchemaElement ((XmlSchemaElement)ElementCollection [Element.RefName.Name], ElementType.ELEMENT_TABLE);
140                         }
141                         else if (ElementType.ELEMENT_UNDEFINED != ElType) {
142                                 
143                                 if (ElType == ElementType.ELEMENT_TABLE){
144                                         ReadTable (Element);
145                                         // we have to return else all child element of the table will be computed again.
146                                         return;
147                                 }
148                                 else if (ElType == ElementType.ELEMENT_COLUMN && Table != null)
149                                         ReadColumn (Element, Table);
150                         }
151                         else {
152                                 // this element is undefined, for now
153                                 ElementCollection.Add (Element.Name, Element);
154                         }
155
156                         // Read Element type
157                         if (Element.SchemaType != null)
158                                 ReadXmlSchemaType (Element.SchemaType);
159
160                         // Read possible constraints
161                         if (Element.Constraints != null && Element.Constraints.Count > 0){
162                                 ReadXmlSchemaConstraints (Element.Constraints);
163                         }
164                 }
165
166                 private void ReadTable (XmlSchemaElement Element)
167                 {
168                         DataTable TempTable = null;
169                         // Add the table to the DataSet only if it is not already in there.
170                         if (!DSet.Tables.Contains(Element.Name)) {
171                                 TempTable = new DataTable (Element.Name);
172                                 DSet.Tables.Add (TempTable);
173                                 ReadXmlSchemaType (Element.SchemaType, TempTable);
174                         }                       
175                 }
176
177                 private void ReadColumn (XmlSchemaElement Element, DataTable Table)
178                 {
179                         DataColumn Column = new DataColumn (Element.Name);
180                         Table.Columns.Add (Column);
181
182                         if (Element.UnhandledAttributes != null) {
183
184                                 foreach (XmlAttribute Attr in Element.UnhandledAttributes) {
185                                         
186                                         switch (Attr.LocalName) {
187                                                 
188                                         case "Caption":
189                                                 Column.Caption = Attr.Value;
190                                                 break;
191                                         case "DataType":
192                                                 Column.DataType = Type.GetType (Attr.Value);
193                                                 break;
194                                         case "type":
195                                                 // FIXME:
196                                                 break;                                          
197                                         default:
198                                                 break;
199                                         }
200                                 }
201                         }
202
203                         //
204                         // Handel rest of the parameters
205                         //
206
207                         if (Column.DataType == null)
208                                 Column.DataType = Type.GetType ("System.String");
209                         
210                         if (Element.DefaultValue != null)
211                                 Column.DefaultValue = Element.DefaultValue;
212
213                         // If Element have type
214                         if (Element.SchemaType != null)
215                                 ReadXmlSchemaType (Element.SchemaType, Column);
216                 }
217
218                 // Makes new Hashtable of the attributes.
219                 private Hashtable ReadUnhandledAttributes (XmlAttribute [] Attributes)
220                 {
221                         Hashtable Result = new Hashtable ();
222
223                         if (Attributes == null)
224                                 return Result;
225
226                         foreach (XmlAttribute attribute in Attributes) {
227                                 Result.Add (attribute.LocalName, attribute.Value);
228                         }
229                         
230                         return Result;
231                 }
232
233                 private void ReadXmlSchemaConstraints (XmlSchemaObjectCollection Constraints)
234                 {
235                         foreach (XmlSchemaObject Constraint in Constraints) {
236                                 if (Constraint is XmlSchemaUnique)
237                                         ReadXmlSchemaUnique ((XmlSchemaUnique)Constraint);
238                                 if (Constraint is XmlSchemaKeyref)
239                                         ReadXmlSchemaKeyref ((XmlSchemaKeyref)Constraint, Constraints);
240                         }
241                 }
242
243                 [MonoTODO()]
244                 private void ReadXmlSchemaUnique (XmlSchemaUnique Unique)
245                 {
246                         // FIXME: Parsing XPath
247                         string TableName = Unique.Selector.XPath;
248                         if (TableName.StartsWith (".//"))
249                                 TableName = TableName.Substring (3);
250                         DataColumn [] Columns;
251                         if (DSet.Tables.Contains (TableName)) {
252                                 DataTable Table = DSet.Tables [TableName];
253                                 Columns = new DataColumn [Unique.Fields.Count];
254                                 int i = 0;
255                                 foreach (XmlSchemaXPath Field in Unique.Fields) {
256                                         if (Table.Columns.Contains (Field.XPath)) {
257                                                 Table.Columns [Field.XPath].Unique = true;
258                                                 Columns [i] = Table.Columns [Field.XPath];
259                                                 i++;
260                                         }
261                                 }
262
263                                 // find if there is an attribute with the constraint name
264                                 // if not use the XmlSchemaUnique name.
265                                 string constraintName = Unique.Name;
266                                 if (Unique.UnhandledAttributes != null){
267                                         foreach (XmlAttribute attr in Unique.UnhandledAttributes){
268                                                 if (attr.LocalName == "ConstraintName"){
269                                                         constraintName = attr.Value;
270                                                         break;
271                                                 }
272                                         }
273                                 }
274                                 UniqueConstraint Constraint = new UniqueConstraint (constraintName, Columns);
275                         }
276                 }
277
278                 [MonoTODO()]
279                 private void ReadXmlSchemaKeyref (XmlSchemaKeyref KeyRef, XmlSchemaObjectCollection collection) {
280                         
281                         string TableName = KeyRef.Selector.XPath;
282                         if (TableName.StartsWith (".//"))
283                                 TableName = TableName.Substring (3);
284                         DataColumn [] Columns;
285                         if (DSet.Tables.Contains (TableName)) {
286                                 DataTable Table = DSet.Tables [TableName];
287                                 Columns = new DataColumn [KeyRef.Fields.Count];
288                                 int i = 0;
289                                 foreach (XmlSchemaXPath Field in KeyRef.Fields) {
290                                         if (Table.Columns.Contains (Field.XPath)) {
291                                                 Columns [i] = Table.Columns [Field.XPath];
292                                                 i++;
293                                         }
294                                 }
295                                 string name = KeyRef.Refer.Name;
296                                 // get the unique constraint for the releation
297                                 UniqueConstraint constraint = GetDSConstraint(name, collection);
298                                 DataRelation relation = new DataRelation(KeyRef.Name, constraint.Columns, Columns);
299                                 if (KeyRef.UnhandledAttributes != null){
300                                         foreach (XmlAttribute attr in KeyRef.UnhandledAttributes){
301                                                 if (attr.LocalName == "IsNested"){
302                                                         if (attr.Value == "true")
303                                                                 relation.Nested = true;
304                                                 }
305                                         }
306                                 }
307
308                                 DSet.Relations.Add(relation);
309                         }
310                 }
311                 
312                 // get the unique constraint for the relation.
313                 // name - the name of the XmlSchemaUnique element
314                 private UniqueConstraint GetDSConstraint(string name, XmlSchemaObjectCollection collection)
315                 {
316                         // find the element in the constraint collection.
317                         foreach (XmlSchemaObject shemaObj in collection){
318                                 if (shemaObj is XmlSchemaUnique){
319                                         XmlSchemaUnique unique = (XmlSchemaUnique) shemaObj;
320                                         if (unique.Name == name){
321                                                 string tableName = unique.Selector.XPath;
322                                                 if (tableName.StartsWith (".//"))
323                                                         tableName = tableName.Substring (3);
324                                                 
325                                                 // find the table in the dataset.
326                                                 if (DSet.Tables.Contains(tableName)){
327                                                         DataTable table = DSet.Tables[tableName];
328                                                         string constraintName = unique.Name;
329                                                         // find if there is an attribute with the constraint name
330                                                         // if not use the XmlSchemaUnique name.
331                                                         if (unique.UnhandledAttributes != null){
332                                                                 foreach (XmlAttribute attr in unique.UnhandledAttributes){
333                                                                         if (attr.LocalName == "ConstraintName"){
334                                                                                 constraintName = attr.Value;
335                                                                                 break;
336                                                                         }
337                                                                 }
338                                                         }
339                                                         if (table.Constraints.Contains(constraintName))
340                                                                 return (UniqueConstraint)table.Constraints[constraintName];
341                                                 }
342
343                                         }
344                                 }
345                         }
346                         return null;
347                 }
348
349
350                 #endregion // Private methods
351
352                 #region Private listeners
353
354                 private void OnXmlSchemaValidation (object sender, ValidationEventArgs args)
355                 {
356                         ;
357                 }
358
359                 #endregion // Private listeners
360
361                 #region Private TypeReaders
362
363                 // Reads XmlSchemaType
364                 private void ReadXmlSchemaType (XmlSchemaType SchemaType)
365                 {
366                         ReadXmlSchemaType (SchemaType, (DataTable)null);
367                 }
368
369                 // Reads XmlSchemaType and decides is it Complex or Simple and continue reading those types
370                 private void ReadXmlSchemaType (XmlSchemaType SchemaType, DataTable Table)
371                 {
372                         if (SchemaType is XmlSchemaComplexType)
373                                 ReadXmlSchemaComplexType ((XmlSchemaComplexType)SchemaType, Table);
374                         else if (SchemaType is XmlSchemaSimpleType)
375                                 ReadXmlSchemaSimpleType ((XmlSchemaSimpleType)SchemaType, Table);
376                 }
377
378                 // Same as above but with DataColumn
379                 private void ReadXmlSchemaType (XmlSchemaType SchemaType, DataColumn Column)
380                 {
381                         if (SchemaType is XmlSchemaComplexType)
382                                 ReadXmlSchemaComplexType ((XmlSchemaComplexType)SchemaType, Column);
383                         else if (SchemaType is XmlSchemaSimpleType)
384                                 ReadXmlSchemaSimpleType ((XmlSchemaSimpleType)SchemaType, Column);
385                 }
386
387                 #endregion // PrivateTypeReader
388
389                 #region TypeReaderHelppers
390
391                 private void ReadXmlSchemaSimpleType (XmlSchemaSimpleType SimpleType, DataColumn Column)
392                 {                       
393                         // Read Contents
394                         if (SimpleType.Content is XmlSchemaSimpleTypeRestriction)
395                                 ReadXmlSchemaSimpleTypeRestriction ((XmlSchemaSimpleTypeRestriction)SimpleType.Content, Column);
396                 }
397
398                 [MonoTODO]
399                 private void ReadXmlSchemaSimpleType (XmlSchemaSimpleType SimpleType, DataTable Table)
400                 {
401                         // TODO: Is it possible that Table-element have simpletype???
402                 }
403
404                 [MonoTODO]
405                 private void ReadXmlSchemaSimpleTypeRestriction (XmlSchemaSimpleTypeRestriction Restriction, DataColumn Column)
406                 {
407                         foreach (XmlSchemaObject Facet in Restriction.Facets) {
408                                 
409                                 // FIXME: I dont know are everyone of these needed but, let them be here for now
410                                 if (Facet is XmlSchemaMaxLengthFacet) 
411                                         Column.MaxLength = Int32.Parse(((XmlSchemaFacet)Facet).Value);
412                                 //else if (Facet is XmlSchemaMinLengthFacet) 
413                                 //      ;
414                                 //else if (Facet is XmlSchemaLengthFacet)
415                                 //      ;
416                                 //else if (Facet is XmlSchemaPatternFacet)
417                                 //      ;
418                                 //else if (Facet is XmlSchemaEnumerationFacet)
419                                 //      ;
420                                 //else if (Facet is XmlSchemaMaxInclusiveFacet)
421                                 //      ;
422                                 //else if (Facet is XmlSchemaMaxExclusiveFacet)
423                                 //      ;
424                                 //else if (Facet is XmlSchemaMinInclusiveFacet)
425                                 //      ;
426                                 //else if (Facet is XmlSchemaMinExclusiveFacet)
427                                 //      ;
428                                 //else if (Facet is XmlSchemaFractionDigitsFacet)
429                                 //      ;
430                                 //else if (Facet is XmlSchemaTotalDigitsFacet)
431                                 //      ;
432                                 //else if (Facet is XmlSchemaWhiteSpaceFacet)
433                                 //      ;
434                         }
435                 }
436
437                 [MonoTODO]
438                 private void ReadXmlSchemaComplexType (XmlSchemaComplexType Type, DataColumn Column)
439                 {
440                         // TODO: is it possible that column-element have complextype
441                 }
442
443                 // Reads XmlSchemaComplexType with DataTable
444                 private void ReadXmlSchemaComplexType (XmlSchemaComplexType Type, DataTable Table)
445                 {
446                         XmlSchemaComplexType ComplexType = Type as XmlSchemaComplexType;
447                         
448                         if (ComplexType.Name != null && ComplexType.Name != string.Empty) {
449
450                                 if (ElementCollection.Contains (ComplexType.Name)) {
451
452                                         if (ComplexType.Particle is XmlSchemaChoice) {
453                                                 ReadXmlSchemaChoice (ComplexType.Particle as XmlSchemaChoice);
454                                         }
455                                         else if (ComplexType.Particle is XmlSchemaSequence) {
456
457                                                 DataTable TempTable = ElementCollection [ComplexType.Name] as DataTable;
458                                                 ElementCollection.Remove (ComplexType.Name);
459                                                 ReadXmlSchemaSequence (ComplexType.Particle as XmlSchemaSequence, TempTable);
460                                         }
461                                 }
462                                 else if (ComplexType.Name != null && !TypeCollection.Contains (ComplexType.Name)) {
463                                         TypeCollection.Add (ComplexType.Name, ComplexType);
464                                 } 
465                                 else {
466
467                                         // If we are here it means that types of elements are Tables :-P
468                                         if (ComplexType.Particle is XmlSchemaSequence)
469                                                 ReadXmlSchemaSequence (ComplexType.Particle as XmlSchemaSequence, Table);
470                                 }
471
472                         }
473                         else {
474                                 XmlSchemaParticle Particle;
475                                 if ((Particle = ComplexType.Particle as XmlSchemaChoice) != null) {
476                                         ReadXmlSchemaChoice (Particle as XmlSchemaChoice);
477                                 }
478                                 else if ((Particle = ComplexType.Particle as XmlSchemaSequence) != null) {
479                                         ReadXmlSchemaSequence (Particle as XmlSchemaSequence, Table);
480                                 }
481                         }                               
482                 }
483                 
484                 #endregion // TypeReaderHelppers
485         }
486 }
487