2007-12-18 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / System.Data / System.Data / XmlSchemaWriter.cs
1 //
2 // XmlSchemaWriter.cs - DataSet.WriteXmlSchema() support
3 //
4 // Author:
5 //
6 //      Atsushi Enomoto  <atsushi@ximian.com>
7 //
8 // Original WriteXml/WriteXmlSchema authors are:
9 //      Ville Palo, Alan Tam, Lluis Sanchez and Eran Domb.
10 //
11
12 //
13 // Copyright (C) 2004-05 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 using System.Collections.Specialized;
38 using System.Globalization;
39 using System.Data;
40 using System.Xml;
41
42 namespace System.Data
43 {
44         internal class XmlSchemaWriter
45         {
46                 const string xmlnsxs = System.Xml.Schema.XmlSchema.Namespace;
47
48                 public static void WriteXmlSchema (DataSet dataset,
49                         XmlWriter writer)
50                 {
51                         WriteXmlSchema (dataset, writer, dataset.Tables,
52                                 dataset.Relations);
53                 }
54
55                 public static void WriteXmlSchema (DataSet dataset,
56                         XmlWriter writer, DataTableCollection tables,
57                         DataRelationCollection relations)
58                 {
59                         new XmlSchemaWriter (dataset, writer,
60                                 tables, relations).WriteSchema ();
61                 }
62
63                 internal static void WriteXmlSchema (XmlWriter writer, DataTable[] tables,
64                                                      DataRelation[] relations,
65                                                      string mainDataTable,
66                                                      string dataSetName)
67                 {
68                         new XmlSchemaWriter (writer, tables, relations, mainDataTable, dataSetName).WriteSchema ();
69                 }
70
71                 public XmlSchemaWriter (DataSet dataset,
72                         XmlWriter writer, DataTableCollection tables,
73                         DataRelationCollection relations)
74                 {
75                         dataSetName = dataset.DataSetName;
76                         dataSetNamespace = dataset.Namespace;
77                         dataSetLocale = dataset.Locale;
78                         dataSetProperties = dataset.ExtendedProperties;
79                         w = writer;
80                         if (tables != null) {
81                                 this.tables = new DataTable[tables.Count];
82                                 for(int i=0;i<tables.Count;i++) this.tables[i] = tables[i];
83                         }
84                         if (relations != null) {
85                                 this.relations = new DataRelation[relations.Count];
86                                 for(int i=0;i<relations.Count;i++) this.relations[i] = relations[i];
87                         }
88                 }
89
90                 public XmlSchemaWriter (XmlWriter writer,
91                         DataTable[] tables,
92                         DataRelation[] relations,
93                         string mainDataTable,
94                         string dataSetName)
95                 {
96                         w = writer;
97                         this.tables = tables;
98                         this.relations = relations;
99                         this.mainDataTable = mainDataTable;
100                         this.dataSetName = dataSetName;
101                         this.dataSetProperties = new PropertyCollection();
102                         if (tables[0].DataSet != null) {
103                                 dataSetNamespace = tables[0].DataSet.Namespace;
104                                 dataSetLocale = tables[0].DataSet.Locale;
105                         } else {
106                                 dataSetNamespace = tables[0].Namespace;
107                                 dataSetLocale = tables[0].Locale;
108                         }
109                 }
110
111                 XmlWriter w;
112                 DataTable[] tables;
113                 DataRelation[] relations;
114                 string mainDataTable;
115                 string dataSetName;
116                 string dataSetNamespace;
117                 PropertyCollection dataSetProperties;
118                 CultureInfo dataSetLocale;
119
120                 ArrayList globalTypeTables = new ArrayList ();
121                 Hashtable additionalNamespaces = new Hashtable ();
122
123                 ArrayList annotation = new ArrayList ();
124
125                 public string ConstraintPrefix {
126                         get { return dataSetNamespace != String.Empty ? XmlConstants.TnsPrefix + ':' : String.Empty; }
127                 }
128
129                 // the whole DataSet
130
131                 public void WriteSchema ()
132                 {
133                         ListDictionary names = new ListDictionary ();
134                         ListDictionary includes = new ListDictionary ();
135
136                         // Add namespaces used in DataSet components (tables, columns, ...)
137                         foreach (DataTable dt in tables) {
138                                 foreach (DataColumn col in dt.Columns)
139                                         CheckNamespace (col.Prefix, col.Namespace, names, includes);
140                                 CheckNamespace (dt.Prefix, dt.Namespace, names, includes);
141                         }
142
143                         w.WriteStartElement ("xs", "schema", xmlnsxs);
144                         w.WriteAttributeString ("id", XmlHelper.Encode (dataSetName));
145
146                         if (dataSetNamespace != String.Empty) {
147                                 w.WriteAttributeString ("targetNamespace",
148                                         dataSetNamespace);
149                                 w.WriteAttributeString (
150                                         "xmlns",
151                                         XmlConstants.TnsPrefix,
152                                         XmlConstants.XmlnsNS,
153                                         dataSetNamespace);
154                         }
155                         w.WriteAttributeString ("xmlns", dataSetNamespace);
156
157                         w.WriteAttributeString ("xmlns", "xs",
158                                 XmlConstants.XmlnsNS, xmlnsxs);
159                         w.WriteAttributeString ("xmlns",
160                                 XmlConstants.MsdataPrefix,
161                                 XmlConstants.XmlnsNS,
162                                 XmlConstants.MsdataNamespace);
163
164                         if (CheckExtendedPropertyExists (tables, relations))
165                                 w.WriteAttributeString ("xmlns",
166                                         XmlConstants.MspropPrefix,
167                                         XmlConstants.XmlnsNS,
168                                         XmlConstants.MspropNamespace);
169
170                         if (dataSetNamespace != String.Empty) {
171                                 w.WriteAttributeString ("attributeFormDefault", "qualified");
172                                 w.WriteAttributeString ("elementFormDefault", "qualified");
173                         }
174
175                         foreach (string prefix in names.Keys)
176                                 w.WriteAttributeString ("xmlns", prefix,
177                                         XmlConstants.XmlnsNS,
178                                         names [prefix] as string);
179
180                         if (includes.Count > 0)
181                                 w.WriteComment ("ATTENTION: This schema contains references to other imported schemas");
182                         foreach (string ns in includes.Keys) {
183                                 w.WriteStartElement ("xs", "import", xmlnsxs);
184                                 w.WriteAttributeString ("namespace", ns);
185                                 w.WriteAttributeString ("schemaLocation", includes [ns] as string);
186                                 w.WriteEndElement ();
187                         }
188
189                         WriteDataSetElement ();
190
191                         w.WriteEndElement (); // xs:schema
192
193                         w.Flush ();
194                 }
195
196                 // FIXME: actually there are some cases that this method(ology)
197                 // does not apply.
198                 private void WriteDataSetElement ()
199                 {
200                         w.WriteStartElement ("xs", "element", xmlnsxs);
201                         w.WriteAttributeString ("name", XmlHelper.Encode (dataSetName));
202                         w.WriteAttributeString (XmlConstants.MsdataPrefix,
203                                 "IsDataSet", XmlConstants.MsdataNamespace,
204                                 "true");
205
206                         if(mainDataTable != null && mainDataTable != "")
207                                 w.WriteAttributeString (
208                                         XmlConstants.MsdataPrefix,
209                                         "MainDataTable",
210                                         XmlConstants.MsdataNamespace,
211                                         mainDataTable);
212 #if NET_2_0
213                         if (dataSetLocale == CultureInfo.CurrentCulture) {
214                                 w.WriteAttributeString (
215                                         XmlConstants.MsdataPrefix,
216                                         "UseCurrentLocale",
217                                         XmlConstants.MsdataNamespace,
218                                         "true");
219                         }
220                         else
221 #endif
222                         {
223                                 w.WriteAttributeString (
224                                         XmlConstants.MsdataPrefix,
225                                         "Locale",
226                                         XmlConstants.MsdataNamespace,
227                                         dataSetLocale.Name);
228                         }
229
230                         AddExtendedPropertyAttributes (dataSetProperties);
231
232                         w.WriteStartElement ("xs", "complexType", xmlnsxs);
233                         w.WriteStartElement ("xs", "choice", xmlnsxs);
234                         w.WriteAttributeString ("minOccurs", "0");
235                         w.WriteAttributeString ("maxOccurs", "unbounded");
236
237                         foreach (DataTable table in tables) {
238                                 bool isTopLevel = true;
239                                 foreach (DataRelation rel in table.ParentRelations) {
240                                         if (rel.Nested) {
241                                                 isTopLevel = false;
242                                                 break;
243                                         }
244                                 }
245                                 
246                                 if (isTopLevel) {
247                                         if (dataSetNamespace != table.Namespace) {
248                                                 // <xs:element ref="X:y" />
249                                                 w.WriteStartElement ("xs",
250                                                         "element",
251                                                         xmlnsxs);
252                                                 w.WriteStartAttribute ("ref", String.Empty);
253                                                 w.WriteQualifiedName (XmlHelper.Encode (table.TableName), table.Namespace);
254                                                 w.WriteEndAttribute ();
255                                                 w.WriteEndElement ();
256                                         }
257                                         else
258                                                 WriteTableElement (table);
259                                 }
260                         }
261
262                         w.WriteEndElement (); // choice
263                         w.WriteEndElement (); // complexType
264
265                         WriteConstraints (); // DataSet constraints
266
267                         w.WriteEndElement (); // element
268
269                         if (annotation.Count > 0) {
270                                 w.WriteStartElement ("xs", "annotation", xmlnsxs);
271                                 w.WriteStartElement ("xs", "appinfo", xmlnsxs);
272
273                                 foreach (object o in annotation) {
274                                         if (!(o is DataRelation))
275                                                 continue;
276                                         WriteDataRelationAnnotation ((DataRelation)o);  
277                                 }
278                                 w.WriteEndElement ();
279                                 w.WriteEndElement ();
280                         }
281                 }
282
283                 private void WriteDataRelationAnnotation (DataRelation rel) 
284                 {
285                         String colnames = String.Empty;
286                         w.WriteStartElement (XmlConstants.MsdataPrefix, "Relationship",
287                                  XmlConstants.MsdataNamespace);
288
289                         w.WriteAttributeString ("name", XmlHelper.Encode (rel.RelationName));
290
291                         w.WriteAttributeString (
292                                         XmlConstants.MsdataPrefix,
293                                         "parent",
294                                         XmlConstants.MsdataNamespace,
295                                         XmlHelper.Encode (rel.ParentTable.TableName));
296
297                         w.WriteAttributeString (
298                                         XmlConstants.MsdataPrefix,
299                                         "child",
300                                         XmlConstants.MsdataNamespace,
301                                         XmlHelper.Encode (rel.ChildTable.TableName));
302
303                         colnames = String.Empty;
304                         foreach (DataColumn col in rel.ParentColumns)
305                                 colnames += XmlHelper.Encode (col.ColumnName) + " ";
306                         w.WriteAttributeString (
307                                         XmlConstants.MsdataPrefix,
308                                         "parentkey",
309                                         XmlConstants.MsdataNamespace,
310                                         colnames.TrimEnd ());
311
312                         colnames = String.Empty;
313                         foreach (DataColumn col in rel.ChildColumns)
314                                 colnames += XmlHelper.Encode (col.ColumnName) + " ";
315                         w.WriteAttributeString (
316                                         XmlConstants.MsdataPrefix,
317                                         "childkey",
318                                         XmlConstants.MsdataNamespace,
319                                         colnames.TrimEnd ());
320
321                         w.WriteEndElement ();
322                 }
323
324                 private void WriteConstraints ()
325                 {
326                         ArrayList names = new ArrayList ();
327
328                         // add all unique constraints.
329                         foreach (DataTable table in tables) {
330                                 foreach (Constraint c in table.Constraints) {
331                                         UniqueConstraint u =
332                                                 c as UniqueConstraint;
333                                         if (u != null) {
334                                                 AddUniqueConstraints (u, names);
335                                                 continue;
336                                         }
337
338                                         ForeignKeyConstraint fk = c as ForeignKeyConstraint;
339                                         bool haveConstraint = false;
340                                         if (relations != null) {
341                                                 foreach (DataRelation r in relations)
342                                                         if (r.RelationName == fk.ConstraintName)
343                                                                 haveConstraint = true;
344                                         }
345                                         if (tables.Length > 1 && fk != null && !haveConstraint) {
346                                                 DataRelation rel = new DataRelation (fk.ConstraintName,
347                                                                                 fk.RelatedColumns, fk.Columns);
348                                                 AddForeignKeys (rel, names, true);
349                                                 continue;
350                                         }
351                                 }
352                         }
353
354                         // Add all foriegn key constraints.
355                         if (relations != null)
356                                 foreach (DataRelation rel in relations) {
357                                         if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null) {
358                                                 annotation.Add (rel);
359                                                 continue;
360                                         }
361                                         AddForeignKeys (rel, names,false);
362                                 }
363                 }
364
365                 // Add unique constaraints to the schema.
366                 // return hashtable with the names of all XmlSchemaUnique elements we created.
367                 private void AddUniqueConstraints (UniqueConstraint uniq,
368                         ArrayList names)
369                 {
370                         // if column of the constraint is hidden do not write the constraint.
371                         foreach (DataColumn column in uniq.Columns)
372                                 if (column.ColumnMapping == MappingType.Hidden)
373                                         return; // do nothing
374
375                         w.WriteStartElement ("xs", "unique", xmlnsxs);
376                         // if constaraint name do not exist in the hashtable we can use it.
377                         string name;
378                         if (!names.Contains (uniq.ConstraintName)) {
379                                 name = XmlHelper.Encode (uniq.ConstraintName);
380                                 w.WriteAttributeString ("name", name);
381                         }
382                         // otherwise generate new constraint name for the
383                         // XmlSchemaUnique element.
384                         else {
385                                 name = XmlHelper.Encode (uniq.Table.TableName) + "_" + XmlHelper.Encode (uniq.ConstraintName);
386                                 w.WriteAttributeString ("name", name);
387                                 w.WriteAttributeString (
388                                         XmlConstants.MsdataPrefix,
389                                         XmlConstants.ConstraintName,
390                                         XmlConstants.MsdataNamespace,
391                                         XmlHelper.Encode (uniq.ConstraintName));
392                         }
393                         names.Add (name);
394
395                         if (uniq.IsPrimaryKey) {
396                                 w.WriteAttributeString (
397                                         XmlConstants.MsdataPrefix,
398                                         XmlConstants.PrimaryKey,
399                                         XmlConstants.MsdataNamespace,
400                                         "true");
401                         }
402
403                         AddExtendedPropertyAttributes (uniq.ExtendedProperties);
404
405                         w.WriteStartElement ("xs", "selector",
406                                 xmlnsxs);
407                         w.WriteAttributeString ("xpath", ".//" +
408                                 ConstraintPrefix + XmlHelper.Encode (uniq.Table.TableName));
409                         w.WriteEndElement (); // selector
410                         foreach (DataColumn c in uniq.Columns) {
411                                 w.WriteStartElement ("xs", "field",
412                                         xmlnsxs);
413                                 w.WriteStartAttribute ("xpath", String.Empty);
414                                 if (c.ColumnMapping == MappingType.Attribute)
415                                         w.WriteString ("@");
416                                 w.WriteString (ConstraintPrefix);
417                                 w.WriteString (XmlHelper.Encode (c.ColumnName));
418                                 w.WriteEndAttribute (); // xpath
419                                 w.WriteEndElement (); // field
420                         }
421
422                         w.WriteEndElement (); // unique
423                 }
424
425                 // Add the foriegn keys to the schema.
426                 private void AddForeignKeys (DataRelation rel, ArrayList names, bool isConstraintOnly)
427                 {
428                         // Do nothing if it contains hidden relation
429                         foreach (DataColumn col in rel.ParentColumns)
430                                 if (col.ColumnMapping == MappingType.Hidden)
431                                         return;
432                         foreach (DataColumn col in rel.ChildColumns)
433                                 if (col.ColumnMapping == MappingType.Hidden)
434                                         return;
435
436                         w.WriteStartElement ("xs", "keyref", xmlnsxs);
437                         w.WriteAttributeString ("name", XmlHelper.Encode (rel.RelationName));
438
439                         //ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
440                         UniqueConstraint uqConst = null; 
441
442                         if (isConstraintOnly) {
443                                 foreach (Constraint c in rel.ParentTable.Constraints) {
444                                         uqConst = c as UniqueConstraint;
445                                         if (uqConst == null)
446                                                 continue;
447                                         if (uqConst.Columns == rel.ParentColumns)
448                                                 break;
449                                 }
450                         } 
451                         else
452                                 uqConst = rel.ParentKeyConstraint;
453
454                         string concatName = XmlHelper.Encode (rel.ParentTable.TableName) + "_" + XmlHelper.Encode (uqConst.ConstraintName);
455                         // first try to find the concatenated name. If we didn't find it - use constraint name.
456                         if (names.Contains (concatName)) {
457                                 w.WriteStartAttribute ("refer", String.Empty);
458                                 w.WriteQualifiedName (concatName, dataSetNamespace);
459                                 w.WriteEndAttribute ();
460                         }
461                         else {
462                                 w.WriteStartAttribute ("refer", String.Empty);
463                                 w.WriteQualifiedName (XmlHelper.Encode (uqConst.ConstraintName), dataSetNamespace);
464                                 w.WriteEndAttribute ();
465                         }
466
467                         if (isConstraintOnly)
468                                 w.WriteAttributeString ( XmlConstants.MsdataPrefix,
469                                         XmlConstants.ConstraintOnly,
470                                         XmlConstants.MsdataNamespace,
471                                         "true");
472                         else if (rel.Nested)
473                                 w.WriteAttributeString (
474                                         XmlConstants.MsdataPrefix,
475                                         XmlConstants.IsNested,
476                                         XmlConstants.MsdataNamespace,
477                                         "true");
478
479                         AddExtendedPropertyAttributes (uqConst.ExtendedProperties);
480
481                         w.WriteStartElement ("xs", "selector", xmlnsxs);
482                         w.WriteAttributeString ("xpath", ".//" + 
483                                 ConstraintPrefix + XmlHelper.Encode (rel.ChildTable.TableName));
484                         w.WriteEndElement ();
485
486                         foreach (DataColumn c in rel.ChildColumns) {
487                                 w.WriteStartElement ("xs", "field",
488                                         xmlnsxs);
489                                 w.WriteStartAttribute ("xpath", String.Empty);
490                                 if (c.ColumnMapping == MappingType.Attribute)
491                                         w.WriteString ("@");
492                                 w.WriteString (ConstraintPrefix);
493                                 w.WriteString (XmlHelper.Encode (c.ColumnName));
494                                 w.WriteEndAttribute ();
495                                 w.WriteEndElement (); // field
496                         }
497
498                         w.WriteEndElement (); // keyref
499                 }
500
501                 // ExtendedProperties
502
503                 private bool CheckExtendedPropertyExists (
504                         DataTable[] tables,
505                         DataRelation[] relations)
506                 {
507                         if (dataSetProperties.Count > 0)
508                                 return true;
509                         foreach (DataTable dt in tables) {
510                                 if (dt.ExtendedProperties.Count > 0)
511                                         return true;
512                                 foreach (DataColumn col in dt.Columns)
513                                         if (col.ExtendedProperties.Count > 0)
514                                                 return true;
515                                 foreach (Constraint c in dt.Constraints)
516                                         if (c.ExtendedProperties.Count > 0)
517                                                 return true;
518                         }
519                         if (relations == null)
520                                 return false;
521                         foreach (DataRelation rel in relations)
522                                 if (rel.ExtendedProperties.Count > 0)
523                                         return true;
524                         return false;
525                 }
526
527                 private void AddExtendedPropertyAttributes (PropertyCollection props)
528                 {
529                         // add extended properties to xs:element
530                         foreach (DictionaryEntry de in props) {
531                                 w.WriteStartAttribute (
532                                         XmlConstants.MspropPrefix,
533                                         XmlConvert.EncodeName (de.Key.ToString ()),
534                                         XmlConstants.MspropNamespace);
535                                 if (de.Value != null)
536                                         w.WriteString (
537                                                 DataSet.WriteObjectXml (de.Value));
538                                 w.WriteEndAttribute ();
539                         }
540                 }
541
542                 // Table
543
544                 private void WriteTableElement (DataTable table)
545                 {
546                         w.WriteStartElement ("xs", "element", xmlnsxs);
547                         w.WriteAttributeString ("name", XmlHelper.Encode (table.TableName));
548
549                         AddExtendedPropertyAttributes (table.ExtendedProperties);
550
551                         WriteTableType (table);
552
553                         w.WriteEndElement ();
554                 }
555
556                 private void WriteTableType (DataTable table)
557                 {
558                         ArrayList elements;
559                         ArrayList atts;
560                         DataColumn simple;
561
562                         DataSet.SplitColumns (table, out atts, out elements, out simple);
563
564                         w.WriteStartElement ("xs", "complexType", xmlnsxs);
565
566                         if (simple != null) {
567                                 w.WriteStartElement ("xs", "simpleContent", xmlnsxs);
568                                 // add column name attribute
569                                 w.WriteAttributeString (
570                                         XmlConstants.MsdataPrefix,
571                                         XmlConstants.ColumnName,
572                                         XmlConstants.MsdataNamespace,
573                                         XmlHelper.Encode (simple.ColumnName));
574
575                                 // add ordinal attribute
576                                 w.WriteAttributeString (
577                                         XmlConstants.MsdataPrefix,
578                                         XmlConstants.Ordinal,
579                                         XmlConstants.MsdataNamespace,
580                                         XmlConvert.ToString (simple.Ordinal));
581
582                                 // add extension
583                                 w.WriteStartElement ("xs", "extension",
584                                         xmlnsxs);
585                                 w.WriteStartAttribute ("base", String.Empty);
586                                 WriteQName (MapType (simple.DataType));
587                                 w.WriteEndAttribute ();
588
589                                 WriteTableAttributes (atts);
590
591                                 w.WriteEndElement ();
592                         } else {
593                                 WriteTableAttributes (atts);
594
595                                 if (elements.Count > 0) {
596                                         w.WriteStartElement ("xs", "sequence",
597                                                 xmlnsxs);
598
599                                         foreach (DataColumn col in elements)
600                                                 WriteTableTypeParticles (col);
601
602                                         foreach (DataRelation rel in table.ChildRelations)
603                                                 if (rel.Nested)
604                                                         WriteChildRelations (rel);
605                                         w.WriteEndElement ();
606                                 }
607                         }
608
609                         w.WriteFullEndElement (); // complexType
610                 }
611
612                 private void WriteTableTypeParticles (DataColumn col)
613                 {
614                         w.WriteStartElement ("xs", "element", xmlnsxs);
615                         w.WriteAttributeString ("name", XmlHelper.Encode (col.ColumnName));
616
617                         if (col.ColumnName != col.Caption && col.Caption != String.Empty)
618                                 w.WriteAttributeString (
619                                         XmlConstants.MsdataPrefix,
620                                         XmlConstants.Caption, 
621                                         XmlConstants.MsdataNamespace,
622                                         col.Caption);
623
624                         if (col.AutoIncrement == true)
625                                 w.WriteAttributeString (
626                                         XmlConstants.MsdataPrefix,
627                                         XmlConstants.AutoIncrement,
628                                         XmlConstants.MsdataNamespace,
629                                         "true");
630
631                         if (col.AutoIncrementSeed != 0) {
632                                 w.WriteAttributeString (
633
634                                         XmlConstants.MsdataPrefix,
635                                         XmlConstants.AutoIncrementSeed,
636                                         XmlConstants.MsdataNamespace,
637                                         XmlConvert.ToString (col.AutoIncrementSeed));
638                         }
639
640                         if (col.AutoIncrementStep != 1) {
641                                 w.WriteAttributeString (
642
643                                         XmlConstants.MsdataPrefix,
644                                         XmlConstants.AutoIncrementStep,
645                                         XmlConstants.MsdataNamespace,
646                                         XmlConvert.ToString (col.AutoIncrementStep));
647                         }
648
649                         if (col.DefaultValue.ToString () != String.Empty)
650                                 w.WriteAttributeString ("default",
651                                         DataSet.WriteObjectXml (col.DefaultValue));
652
653                         if (col.ReadOnly)
654                                 w.WriteAttributeString (
655                                         XmlConstants.MsdataPrefix,
656                                         XmlConstants.ReadOnly,
657                                         XmlConstants.MsdataNamespace,
658                                         "true");
659
660                         XmlQualifiedName typeQName = null;
661                         if (col.MaxLength < 0) {
662                                 w.WriteStartAttribute ("type", String.Empty);
663                                 typeQName = MapType (col.DataType);
664                                 WriteQName (typeQName);
665                                 w.WriteEndAttribute ();
666                         }
667
668                         if (typeQName == XmlConstants.QnString
669                                 && col.DataType != typeof (string)
670                                 && col.DataType != typeof (char)) {
671                                 w.WriteStartAttribute (
672                                         XmlConstants.MsdataPrefix,
673                                         XmlConstants.DataType,
674                                         XmlConstants.MsdataNamespace);
675                                 string runtimeName = col.DataType.AssemblyQualifiedName;
676                                 w.WriteString (runtimeName);
677                                 w.WriteEndAttribute ();
678                         }
679
680                         if (col.AllowDBNull)
681                                 w.WriteAttributeString ("minOccurs", "0");
682
683                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
684                         //                            XmlConstants.Ordinal, 
685                         //                            XmlConstants.MsdataNamespace, 
686                         //                            col.Ordinal.ToString ());
687
688                         // Write SimpleType if column have MaxLength
689                         if (col.MaxLength > -1)
690                                 WriteSimpleType (col);
691
692                         AddExtendedPropertyAttributes (col.ExtendedProperties);
693
694                         w.WriteEndElement (); // sequence
695                 }
696
697                 private void WriteChildRelations (DataRelation rel)
698                 {
699                         if (rel.ChildTable.Namespace != dataSetNamespace) {
700                                 w.WriteStartElement ("xs", "element", xmlnsxs);
701                                 w.WriteStartAttribute ("ref", String.Empty);
702                                 w.WriteQualifiedName (
703                                         XmlHelper.Encode (rel.ChildTable.TableName),
704                                         rel.ChildTable.Namespace);
705                                 w.WriteEndAttribute ();
706                         } else {
707                                 w.WriteStartElement ("xs", "element", xmlnsxs);
708                                 w.WriteStartAttribute ("name", String.Empty);
709                                 w.WriteQualifiedName (
710                                         XmlHelper.Encode (rel.ChildTable.TableName),
711                                         rel.ChildTable.Namespace);
712                                 w.WriteEndAttribute ();
713                                 w.WriteAttributeString ("minOccurs", "0");
714                                 w.WriteAttributeString ("maxOccurs", "unbounded");
715
716                                 globalTypeTables.Add (rel.ChildTable);
717                         }
718                         WriteTableType (rel.ChildTable);
719                         w.WriteEndElement ();
720                 }
721
722                 private void WriteTableAttributes (ArrayList atts)
723                 {
724                         //Then a list of attributes
725                         foreach (DataColumn col in atts) {
726                                 w.WriteStartElement ("xs", "attribute", xmlnsxs);
727
728                                 string name = XmlHelper.Encode (col.ColumnName);
729                                 if (col.Namespace != String.Empty) {
730                                         w.WriteAttributeString ("form", "qualified");
731                                         string prefix = col.Prefix == String.Empty ? "app" + additionalNamespaces.Count : col.Prefix;
732                                         name = prefix + ":" + name;
733                                         // FIXME: Handle prefix mapping correctly.
734                                         additionalNamespaces [prefix] = col.Namespace;
735                                 }
736                                 w.WriteAttributeString ("name", name);
737
738                                 AddExtendedPropertyAttributes (
739                                         col.ExtendedProperties);
740
741                                 if (col.ReadOnly)
742                                         w.WriteAttributeString (
743                                                 XmlConstants.MsdataPrefix,
744                                                 XmlConstants.ReadOnly,
745                                                 XmlConstants.MsdataNamespace,
746                                                 "true");
747
748                                 if (col.MaxLength < 0) {
749                                         // otherwise simpleType is written later
750                                         w.WriteStartAttribute ("type", String.Empty);
751                                         WriteQName (MapType (col.DataType));
752                                         w.WriteEndAttribute ();
753                                 }
754
755                                 if (!col.AllowDBNull)
756                                         w.WriteAttributeString ("use", "required");
757                                 if (col.DefaultValue.ToString () != String.Empty)
758                                         w.WriteAttributeString ("default",
759                                                 DataSet.WriteObjectXml (col.DefaultValue));
760
761                                 if (col.MaxLength > -1)
762                                         WriteSimpleType (col);
763
764                                 w.WriteEndElement (); // attribute
765                         }
766                 }
767
768                 private void WriteSimpleType (DataColumn col)
769                 {
770                         w.WriteStartElement ("xs", "simpleType", xmlnsxs);
771                         w.WriteStartElement ("xs", "restriction", xmlnsxs);
772                         w.WriteStartAttribute ("base", String.Empty);
773                         WriteQName (MapType (col.DataType));
774                         w.WriteEndAttribute ();
775
776                         w.WriteStartElement ("xs", "maxLength", xmlnsxs);
777                         w.WriteAttributeString ("value",
778                                 XmlConvert.ToString (col.MaxLength));
779                         w.WriteEndElement (); // maxLength
780                         w.WriteEndElement (); // restriction
781                         w.WriteEndElement (); // simpleType
782                 }
783
784                 private void WriteQName (XmlQualifiedName name)
785                 {
786                         w.WriteQualifiedName (name.Name, name.Namespace);
787                 }
788
789                 private void CheckNamespace (string prefix, string ns, ListDictionary names, ListDictionary includes)
790                 {
791                         if (ns == String.Empty)
792                                 return;
793                         if (dataSetNamespace != ns) {
794                                 if ((string)names [prefix] != ns) {
795                                         for (int i = 1; i < int.MaxValue; i++) {
796                                                 string p = "app" + i;
797                                                 if (names [p] == null) {
798                                                         names.Add (p, ns);
799                                                         HandleExternalNamespace (p, ns, includes);
800                                                         break;
801                                                 }
802                                         }
803                                 }
804                         }
805                 }
806
807                 private void HandleExternalNamespace (string prefix, string ns, ListDictionary includes)
808                 {
809                         if (includes.Contains (ns))
810                                 return; // nothing to do
811                         includes.Add (ns, "_" + prefix + ".xsd");
812                 }
813
814                 private /*static*/ XmlQualifiedName MapType (Type type)
815                 {
816                         switch (Type.GetTypeCode (type)) {
817                                 case TypeCode.String: return XmlConstants.QnString;
818                                 case TypeCode.Int16: return XmlConstants.QnShort;
819                                 case TypeCode.Int32: return XmlConstants.QnInt;
820                                 case TypeCode.Int64: return XmlConstants.QnLong;
821                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
822                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
823                                 //case TypeCode.Char: return XmlConstants.QnChar;
824                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
825                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
826                                 case TypeCode.Double: return XmlConstants.QnDouble;
827                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
828                                 case TypeCode.Single: return XmlConstants.QnFloat;
829                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
830                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
831                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
832                         }
833                         
834                         if (typeof (TimeSpan) == type)
835                                 return XmlConstants.QnDuration;
836                         else if (typeof (System.Uri) == type)
837                                 return XmlConstants.QnUri;
838                         else if (typeof (byte[]) == type)
839                                 return XmlConstants.QnBase64Binary;
840                         else if (typeof (XmlQualifiedName) == type)
841                                 return XmlConstants.QnXmlQualifiedName;
842                         else
843                                 return XmlConstants.QnString;
844                 }
845         }
846 }