2006-07-11 Senganal T <tsenganal@novell.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                 public XmlSchemaWriter (DataSet dataset,
64                         XmlWriter writer, DataTableCollection tables,
65                         DataRelationCollection relations)
66                 {
67                         ds = dataset;
68                         w = writer;
69                         this.tables = tables;
70                         this.relations = relations;
71                 }
72
73                 DataSet ds;
74                 XmlWriter w;
75                 DataTableCollection tables;
76                 DataRelationCollection relations;
77
78                 ArrayList globalTypeTables = new ArrayList ();
79                 Hashtable additionalNamespaces = new Hashtable ();
80
81                 ArrayList annotation = new ArrayList ();
82
83                 public string ConstraintPrefix {
84                         get { return ds.Namespace != String.Empty ? XmlConstants.TnsPrefix + ':' : String.Empty; }
85                 }
86
87                 // the whole DataSet
88
89                 public void WriteSchema ()
90                 {
91                         ListDictionary names = new ListDictionary ();
92                         ListDictionary includes = new ListDictionary ();
93
94                         // Add namespaces used in DataSet components (tables, columns, ...)
95                         foreach (DataTable dt in tables) {
96                                 foreach (DataColumn col in dt.Columns)
97                                         CheckNamespace (col.Prefix, col.Namespace, names, includes);
98                                 CheckNamespace (dt.Prefix, dt.Namespace, names, includes);
99                         }
100
101                         w.WriteStartElement ("xs", "schema", xmlnsxs);
102                         w.WriteAttributeString ("id", XmlHelper.Encode (ds.DataSetName));
103
104                         if (ds.Namespace != String.Empty) {
105                                 w.WriteAttributeString ("targetNamespace",
106                                         ds.Namespace);
107                                 w.WriteAttributeString (
108                                         "xmlns",
109                                         XmlConstants.TnsPrefix,
110                                         XmlConstants.XmlnsNS,
111                                         ds.Namespace);
112                         }
113                         w.WriteAttributeString ("xmlns", ds.Namespace);
114
115                         w.WriteAttributeString ("xmlns", "xs",
116                                 XmlConstants.XmlnsNS, xmlnsxs);
117                         w.WriteAttributeString ("xmlns",
118                                 XmlConstants.MsdataPrefix,
119                                 XmlConstants.XmlnsNS,
120                                 XmlConstants.MsdataNamespace);
121
122                         if (CheckExtendedPropertyExists (tables, relations))
123                                 w.WriteAttributeString ("xmlns",
124                                         XmlConstants.MspropPrefix,
125                                         XmlConstants.XmlnsNS,
126                                         XmlConstants.MspropNamespace);
127
128                         if (ds.Namespace != String.Empty) {
129                                 w.WriteAttributeString ("attributeFormDefault", "qualified");
130                                 w.WriteAttributeString ("elementFormDefault", "qualified");
131                         }
132
133                         foreach (string prefix in names.Keys)
134                                 w.WriteAttributeString ("xmlns", prefix,
135                                         XmlConstants.XmlnsNS,
136                                         names [prefix] as string);
137
138                         if (includes.Count > 0)
139                                 w.WriteComment ("ATTENTION: This schema contains references to other imported schemas");
140                         foreach (string ns in includes.Keys) {
141                                 w.WriteStartElement ("xs", "import", xmlnsxs);
142                                 w.WriteAttributeString ("namespace", ns);
143                                 w.WriteAttributeString ("schemaLocation", includes [ns] as string);
144                                 w.WriteEndElement ();
145                         }
146
147                         WriteDataSetElement ();
148
149                         w.WriteEndElement (); // xs:schema
150
151                         w.Flush ();
152                 }
153
154                 // FIXME: actually there are some cases that this method(ology)
155                 // does not apply.
156                 private void WriteDataSetElement ()
157                 {
158                         w.WriteStartElement ("xs", "element", xmlnsxs);
159                         w.WriteAttributeString ("name", XmlHelper.Encode (ds.DataSetName));
160                         w.WriteAttributeString (XmlConstants.MsdataPrefix,
161                                 "IsDataSet", XmlConstants.MsdataNamespace,
162                                 "true");
163 #if NET_2_0
164                         if (ds.Locale == CultureInfo.CurrentCulture) {
165                                 w.WriteAttributeString (
166                                         XmlConstants.MsdataPrefix,
167                                         "UseCurrentCulture",
168                                         XmlConstants.MsdataNamespace,
169                                         "true");
170                         }
171                         else
172 #endif
173                         {
174                                 w.WriteAttributeString (
175                                         XmlConstants.MsdataPrefix,
176                                         "Locale",
177                                         XmlConstants.MsdataNamespace,
178                                         ds.Locale.Name);
179                         }
180
181                         AddExtendedPropertyAttributes (ds.ExtendedProperties);
182
183                         w.WriteStartElement ("xs", "complexType", xmlnsxs);
184                         w.WriteStartElement ("xs", "choice", xmlnsxs);
185                         w.WriteAttributeString ("maxOccurs", "unbounded");
186
187                         foreach (DataTable table in tables) {
188                                 bool isTopLevel = true;
189                                 foreach (DataRelation rel in table.ParentRelations) {
190                                         if (rel.Nested) {
191                                                 isTopLevel = false;
192                                                 break;
193                                         }
194                                 }
195                                 
196                                 if (isTopLevel) {
197                                         if (ds.Namespace != table.Namespace) {
198                                                 // <xs:element ref="X:y" />
199                                                 w.WriteStartElement ("xs",
200                                                         "element",
201                                                         xmlnsxs);
202                                                 w.WriteStartAttribute ("ref", String.Empty);
203                                                 w.WriteQualifiedName (XmlHelper.Encode (table.TableName), table.Namespace);
204                                                 w.WriteEndAttribute ();
205                                                 w.WriteEndElement ();
206                                         }
207                                         else
208                                                 WriteTableElement (table);
209                                 }
210                         }
211
212                         w.WriteEndElement (); // choice
213                         w.WriteEndElement (); // complexType
214
215                         WriteConstraints (); // DataSet constraints
216
217                         w.WriteEndElement (); // element
218
219                         if (annotation.Count > 0) {
220                                 w.WriteStartElement ("xs", "annotation", xmlnsxs);
221                                 w.WriteStartElement ("xs", "appinfo", xmlnsxs);
222
223                                 foreach (object o in annotation) {
224                                         if (!(o is DataRelation))
225                                                 continue;
226                                         WriteDataRelationAnnotation ((DataRelation)o);  
227                                 }
228                                 w.WriteEndElement ();
229                                 w.WriteEndElement ();
230                         }
231                 }
232
233                 private void WriteDataRelationAnnotation (DataRelation rel) 
234                 {
235                         String colnames = String.Empty;
236                         w.WriteStartElement (XmlConstants.MsdataPrefix, "Relationship",
237                                  XmlConstants.MsdataNamespace);
238
239                         w.WriteAttributeString ("name", XmlHelper.Encode (rel.RelationName));
240
241                         w.WriteAttributeString (
242                                         XmlConstants.MsdataPrefix,
243                                         "parent",
244                                         XmlConstants.MsdataNamespace,
245                                         XmlHelper.Encode (rel.ParentTable.TableName));
246
247                         w.WriteAttributeString (
248                                         XmlConstants.MsdataPrefix,
249                                         "child",
250                                         XmlConstants.MsdataNamespace,
251                                         XmlHelper.Encode (rel.ChildTable.TableName));
252
253                         colnames = String.Empty;
254                         foreach (DataColumn col in rel.ParentColumns)
255                                 colnames += XmlHelper.Encode (col.ColumnName) + " ";
256                         w.WriteAttributeString (
257                                         XmlConstants.MsdataPrefix,
258                                         "parentkey",
259                                         XmlConstants.MsdataNamespace,
260                                         colnames.TrimEnd ());
261
262                         colnames = String.Empty;
263                         foreach (DataColumn col in rel.ChildColumns)
264                                 colnames += XmlHelper.Encode (col.ColumnName) + " ";
265                         w.WriteAttributeString (
266                                         XmlConstants.MsdataPrefix,
267                                         "childkey",
268                                         XmlConstants.MsdataNamespace,
269                                         colnames.TrimEnd ());
270
271                         w.WriteEndElement ();
272                 }
273
274                 private void WriteConstraints ()
275                 {
276                         ArrayList names = new ArrayList ();
277
278                         // add all unique constraints.
279                         foreach (DataTable table in tables) {
280                                 foreach (Constraint c in table.Constraints) {
281                                         UniqueConstraint u =
282                                                 c as UniqueConstraint;
283                                         if (u != null) {
284                                                 AddUniqueConstraints (u, names);
285                                                 continue;
286                                         }
287
288                                         ForeignKeyConstraint fk = c as ForeignKeyConstraint;
289                                         if (fk != null && (relations == null || !(relations.Contains (fk.ConstraintName)))) {
290                                                 DataRelation rel = new DataRelation (fk.ConstraintName,
291                                                                                 fk.RelatedColumns, fk.Columns);
292                                                 AddForeignKeys (rel, names, true);
293                                                 continue;
294                                         }
295                                 }
296                         }
297
298                         // Add all foriegn key constraints.
299                         if (relations != null)
300                                 foreach (DataRelation rel in relations) {
301                                         if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null) {
302                                                 annotation.Add (rel);
303                                                 continue;
304                                         }
305                                         AddForeignKeys (rel, names,false);
306                                 }
307                 }
308
309                 // Add unique constaraints to the schema.
310                 // return hashtable with the names of all XmlSchemaUnique elements we created.
311                 private void AddUniqueConstraints (UniqueConstraint uniq,
312                         ArrayList names)
313                 {
314                         // if column of the constraint is hidden do not write the constraint.
315                         foreach (DataColumn column in uniq.Columns)
316                                 if (column.ColumnMapping == MappingType.Hidden)
317                                         return; // do nothing
318
319                         w.WriteStartElement ("xs", "unique", xmlnsxs);
320                         // if constaraint name do not exist in the hashtable we can use it.
321                         string name;
322                         if (!names.Contains (uniq.ConstraintName)) {
323                                 name = XmlHelper.Encode (uniq.ConstraintName);
324                                 w.WriteAttributeString ("name", name);
325                         }
326                         // otherwise generate new constraint name for the
327                         // XmlSchemaUnique element.
328                         else {
329                                 name = XmlHelper.Encode (uniq.Table.TableName) + "_" + XmlHelper.Encode (uniq.ConstraintName);
330                                 w.WriteAttributeString ("name", name);
331                                 w.WriteAttributeString (
332                                         XmlConstants.MsdataPrefix,
333                                         XmlConstants.ConstraintName,
334                                         XmlConstants.MsdataNamespace,
335                                         XmlHelper.Encode (uniq.ConstraintName));
336                         }
337                         names.Add (name);
338
339                         if (uniq.IsPrimaryKey) {
340                                 w.WriteAttributeString (
341                                         XmlConstants.MsdataPrefix,
342                                         XmlConstants.PrimaryKey,
343                                         XmlConstants.MsdataNamespace,
344                                         "true");
345                         }
346
347                         AddExtendedPropertyAttributes (uniq.ExtendedProperties);
348
349                         w.WriteStartElement ("xs", "selector",
350                                 xmlnsxs);
351                         w.WriteAttributeString ("xpath", ".//" +
352                                 ConstraintPrefix + XmlHelper.Encode (uniq.Table.TableName));
353                         w.WriteEndElement (); // selector
354                         foreach (DataColumn c in uniq.Columns) {
355                                 w.WriteStartElement ("xs", "field",
356                                         xmlnsxs);
357                                 w.WriteStartAttribute ("xpath", String.Empty);
358                                 if (c.ColumnMapping == MappingType.Attribute)
359                                         w.WriteString ("@");
360                                 w.WriteString (ConstraintPrefix);
361                                 w.WriteString (XmlHelper.Encode (c.ColumnName));
362                                 w.WriteEndAttribute (); // xpath
363                                 w.WriteEndElement (); // field
364                         }
365
366                         w.WriteEndElement (); // unique
367                 }
368
369                 // Add the foriegn keys to the schema.
370                 private void AddForeignKeys (DataRelation rel, ArrayList names, bool isConstraintOnly)
371                 {
372                         // Do nothing if it contains hidden relation
373                         foreach (DataColumn col in rel.ParentColumns)
374                                 if (col.ColumnMapping == MappingType.Hidden)
375                                         return;
376                         foreach (DataColumn col in rel.ChildColumns)
377                                 if (col.ColumnMapping == MappingType.Hidden)
378                                         return;
379
380                         w.WriteStartElement ("xs", "keyref", xmlnsxs);
381                         w.WriteAttributeString ("name", XmlHelper.Encode (rel.RelationName));
382
383                         //ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
384                         UniqueConstraint uqConst = null; 
385
386                         if (isConstraintOnly) {
387                                 foreach (Constraint c in rel.ParentTable.Constraints) {
388                                         uqConst = c as UniqueConstraint;
389                                         if (uqConst == null)
390                                                 continue;
391                                         if (uqConst.Columns == rel.ParentColumns)
392                                                 break;
393                                 }
394                         } 
395                         else
396                                 uqConst = rel.ParentKeyConstraint;
397
398                         string concatName = XmlHelper.Encode (rel.ParentTable.TableName) + "_" + XmlHelper.Encode (uqConst.ConstraintName);
399                         // first try to find the concatenated name. If we didn't find it - use constraint name.
400                         if (names.Contains (concatName)) {
401                                 w.WriteStartAttribute ("refer", String.Empty);
402                                 w.WriteQualifiedName (concatName, ds.Namespace);
403                                 w.WriteEndAttribute ();
404                         }
405                         else {
406                                 w.WriteStartAttribute ("refer", String.Empty);
407                                 w.WriteQualifiedName (XmlHelper.Encode (uqConst.ConstraintName), ds.Namespace);
408                                 w.WriteEndAttribute ();
409                         }
410
411                         if (isConstraintOnly)
412                                 w.WriteAttributeString ( XmlConstants.MsdataPrefix,
413                                         XmlConstants.ConstraintOnly,
414                                         XmlConstants.MsdataNamespace,
415                                         "true");
416                         else if (rel.Nested)
417                                 w.WriteAttributeString (
418                                         XmlConstants.MsdataPrefix,
419                                         XmlConstants.IsNested,
420                                         XmlConstants.MsdataNamespace,
421                                         "true");
422
423                         AddExtendedPropertyAttributes (uqConst.ExtendedProperties);
424
425                         w.WriteStartElement ("xs", "selector", xmlnsxs);
426                         w.WriteAttributeString ("xpath", ".//" + 
427                                 ConstraintPrefix + XmlHelper.Encode (rel.ChildTable.TableName));
428                         w.WriteEndElement ();
429
430                         foreach (DataColumn c in rel.ChildColumns) {
431                                 w.WriteStartElement ("xs", "field",
432                                         xmlnsxs);
433                                 w.WriteStartAttribute ("xpath", String.Empty);
434                                 if (c.ColumnMapping == MappingType.Attribute)
435                                         w.WriteString ("@");
436                                 w.WriteString (ConstraintPrefix);
437                                 w.WriteString (XmlHelper.Encode (c.ColumnName));
438                                 w.WriteEndAttribute ();
439                                 w.WriteEndElement (); // field
440                         }
441
442                         w.WriteEndElement (); // keyref
443                 }
444
445                 // ExtendedProperties
446
447                 private bool CheckExtendedPropertyExists (
448                         DataTableCollection tables,
449                         DataRelationCollection relations)
450                 {
451                         if (ds.ExtendedProperties.Count > 0)
452                                 return true;
453                         foreach (DataTable dt in tables) {
454                                 if (dt.ExtendedProperties.Count > 0)
455                                         return true;
456                                 foreach (DataColumn col in dt.Columns)
457                                         if (col.ExtendedProperties.Count > 0)
458                                                 return true;
459                                 foreach (Constraint c in dt.Constraints)
460                                         if (c.ExtendedProperties.Count > 0)
461                                                 return true;
462                         }
463                         if (relations == null)
464                                 return false;
465                         foreach (DataRelation rel in relations)
466                                 if (rel.ExtendedProperties.Count > 0)
467                                         return true;
468                         return false;
469                 }
470
471                 private void AddExtendedPropertyAttributes (PropertyCollection props)
472                 {
473                         // add extended properties to xs:element
474                         foreach (DictionaryEntry de in props) {
475                                 w.WriteStartAttribute (
476                                         XmlConstants.MspropPrefix,
477                                         XmlConvert.EncodeName (de.Key.ToString ()),
478                                         XmlConstants.MspropNamespace);
479                                 if (de.Value != null)
480                                         w.WriteString (
481                                                 DataSet.WriteObjectXml (de.Value));
482                                 w.WriteEndAttribute ();
483                         }
484                 }
485
486                 // Table
487
488                 private void WriteTableElement (DataTable table)
489                 {
490                         w.WriteStartElement ("xs", "element", xmlnsxs);
491                         w.WriteAttributeString ("name", XmlHelper.Encode (table.TableName));
492
493                         AddExtendedPropertyAttributes (table.ExtendedProperties);
494
495                         WriteTableType (table);
496
497                         w.WriteEndElement ();
498                 }
499
500                 private void WriteTableType (DataTable table)
501                 {
502                         ArrayList elements;
503                         ArrayList atts;
504                         DataColumn simple;
505
506                         DataSet.SplitColumns (table, out atts, out elements, out simple);
507
508                         w.WriteStartElement ("xs", "complexType", xmlnsxs);
509
510                         if (simple != null) {
511                                 w.WriteStartElement ("xs", "simpleContent", xmlnsxs);
512                                 // add column name attribute
513                                 w.WriteAttributeString (
514                                         XmlConstants.MsdataPrefix,
515                                         XmlConstants.ColumnName,
516                                         XmlConstants.MsdataNamespace,
517                                         XmlHelper.Encode (simple.ColumnName));
518
519                                 // add ordinal attribute
520                                 w.WriteAttributeString (
521                                         XmlConstants.MsdataPrefix,
522                                         XmlConstants.Ordinal,
523                                         XmlConstants.MsdataNamespace,
524                                         XmlConvert.ToString (simple.Ordinal));
525
526                                 // add extension
527                                 w.WriteStartElement ("xs", "extension",
528                                         xmlnsxs);
529                                 w.WriteStartAttribute ("base", String.Empty);
530                                 WriteQName (MapType (simple.DataType));
531                                 w.WriteEndAttribute ();
532
533                                 WriteTableAttributes (atts);
534
535                                 w.WriteEndElement ();
536                         } else {
537                                 WriteTableAttributes (atts);
538
539                                 if (elements.Count > 0) {
540                                         w.WriteStartElement ("xs", "sequence",
541                                                 xmlnsxs);
542
543                                         foreach (DataColumn col in elements)
544                                                 WriteTableTypeParticles (col);
545
546                                         foreach (DataRelation rel in table.ChildRelations)
547                                                 if (rel.Nested)
548                                                         WriteChildRelations (rel);
549                                         w.WriteEndElement ();
550                                 }
551                         }
552
553                         w.WriteFullEndElement (); // complexType
554                 }
555
556                 private void WriteTableTypeParticles (DataColumn col)
557                 {
558                         w.WriteStartElement ("xs", "element", xmlnsxs);
559                         w.WriteAttributeString ("name", XmlHelper.Encode (col.ColumnName));
560
561                         if (col.ColumnName != col.Caption && col.Caption != String.Empty)
562                                 w.WriteAttributeString (
563                                         XmlConstants.MsdataPrefix,
564                                         XmlConstants.Caption, 
565                                         XmlConstants.MsdataNamespace,
566                                         col.Caption);
567
568                         if (col.AutoIncrement == true)
569                                 w.WriteAttributeString (
570                                         XmlConstants.MsdataPrefix,
571                                         XmlConstants.AutoIncrement,
572                                         XmlConstants.MsdataNamespace,
573                                         "true");
574
575                         if (col.AutoIncrementSeed != 0) {
576                                 w.WriteAttributeString (
577
578                                         XmlConstants.MsdataPrefix,
579                                         XmlConstants.AutoIncrementSeed,
580                                         XmlConstants.MsdataNamespace,
581                                         XmlConvert.ToString (col.AutoIncrementSeed));
582                         }
583
584                         if (col.AutoIncrementStep != 1) {
585                                 w.WriteAttributeString (
586
587                                         XmlConstants.MsdataPrefix,
588                                         XmlConstants.AutoIncrementStep,
589                                         XmlConstants.MsdataNamespace,
590                                         XmlConvert.ToString (col.AutoIncrementStep));
591                         }
592
593                         if (col.DefaultValue.ToString () != String.Empty)
594                                 w.WriteAttributeString ("default",
595                                         DataSet.WriteObjectXml (col.DefaultValue));
596
597                         if (col.ReadOnly)
598                                 w.WriteAttributeString (
599                                         XmlConstants.MsdataPrefix,
600                                         XmlConstants.ReadOnly,
601                                         XmlConstants.MsdataNamespace,
602                                         "true");
603
604                         XmlQualifiedName typeQName = null;
605                         if (col.MaxLength < 0) {
606                                 w.WriteStartAttribute ("type", String.Empty);
607                                 typeQName = MapType (col.DataType);
608                                 WriteQName (typeQName);
609                                 w.WriteEndAttribute ();
610                         }
611
612                         if (typeQName == XmlConstants.QnString
613                                 && col.DataType != typeof (string)
614                                 && col.DataType != typeof (char)) {
615                                 w.WriteStartAttribute (
616                                         XmlConstants.MsdataPrefix,
617                                         XmlConstants.DataType,
618                                         XmlConstants.MsdataNamespace);
619                                 string runtimeName = col.DataType.AssemblyQualifiedName;
620                                 w.WriteString (runtimeName);
621                                 w.WriteEndAttribute ();
622                         }
623
624                         if (col.AllowDBNull)
625                                 w.WriteAttributeString ("minOccurs", "0");
626
627                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
628                         //                            XmlConstants.Ordinal, 
629                         //                            XmlConstants.MsdataNamespace, 
630                         //                            col.Ordinal.ToString ());
631
632                         // Write SimpleType if column have MaxLength
633                         if (col.MaxLength > -1)
634                                 WriteSimpleType (col);
635
636                         AddExtendedPropertyAttributes (col.ExtendedProperties);
637
638                         w.WriteEndElement (); // sequence
639                 }
640
641                 private void WriteChildRelations (DataRelation rel)
642                 {
643                         if (rel.ChildTable.Namespace != ds.Namespace) {
644                                 w.WriteStartElement ("xs", "element", xmlnsxs);
645                                 w.WriteStartAttribute ("ref", String.Empty);
646                                 w.WriteQualifiedName (
647                                         XmlHelper.Encode (rel.ChildTable.TableName),
648                                         rel.ChildTable.Namespace);
649                                 w.WriteEndAttribute ();
650                         } else {
651                                 w.WriteStartElement ("xs", "element", xmlnsxs);
652                                 w.WriteStartAttribute ("name", String.Empty);
653                                 w.WriteQualifiedName (
654                                         XmlHelper.Encode (rel.ChildTable.TableName),
655                                         rel.ChildTable.Namespace);
656                                 w.WriteEndAttribute ();
657                                 w.WriteAttributeString ("minOccurs", "0");
658                                 w.WriteAttributeString ("maxOccurs", "unbounded");
659
660                                 globalTypeTables.Add (rel.ChildTable);
661                         }
662                         WriteTableType (rel.ChildTable);
663                         w.WriteEndElement ();
664                 }
665
666                 private void WriteTableAttributes (ArrayList atts)
667                 {
668                         //Then a list of attributes
669                         foreach (DataColumn col in atts) {
670                                 w.WriteStartElement ("xs", "attribute", xmlnsxs);
671
672                                 string name = XmlHelper.Encode (col.ColumnName);
673                                 if (col.Namespace != String.Empty) {
674                                         w.WriteAttributeString ("form", "qualified");
675                                         string prefix = col.Prefix == String.Empty ? "app" + additionalNamespaces.Count : col.Prefix;
676                                         name = prefix + ":" + name;
677                                         // FIXME: Handle prefix mapping correctly.
678                                         additionalNamespaces [prefix] = col.Namespace;
679                                 }
680                                 w.WriteAttributeString ("name", name);
681
682                                 AddExtendedPropertyAttributes (
683                                         col.ExtendedProperties);
684
685                                 if (col.ReadOnly)
686                                         w.WriteAttributeString (
687                                                 XmlConstants.MsdataPrefix,
688                                                 XmlConstants.ReadOnly,
689                                                 XmlConstants.MsdataNamespace,
690                                                 "true");
691
692                                 if (col.MaxLength < 0) {
693                                         // otherwise simpleType is written later
694                                         w.WriteStartAttribute ("type", String.Empty);
695                                         WriteQName (MapType (col.DataType));
696                                         w.WriteEndAttribute ();
697                                 }
698
699                                 if (!col.AllowDBNull)
700                                         w.WriteAttributeString ("use", "required");
701                                 if (col.DefaultValue.ToString () != String.Empty)
702                                         w.WriteAttributeString ("default",
703                                                 DataSet.WriteObjectXml (col.DefaultValue));
704
705                                 if (col.MaxLength > -1)
706                                         WriteSimpleType (col);
707
708                                 w.WriteEndElement (); // attribute
709                         }
710                 }
711
712                 private void WriteSimpleType (DataColumn col)
713                 {
714                         w.WriteStartElement ("xs", "simpleType", xmlnsxs);
715                         w.WriteStartElement ("xs", "restriction", xmlnsxs);
716                         w.WriteStartAttribute ("base", String.Empty);
717                         WriteQName (MapType (col.DataType));
718                         w.WriteEndAttribute ();
719
720                         w.WriteStartElement ("xs", "maxLength", xmlnsxs);
721                         w.WriteAttributeString ("value",
722                                 XmlConvert.ToString (col.MaxLength));
723                         w.WriteEndElement (); // maxLength
724                         w.WriteEndElement (); // restriction
725                         w.WriteEndElement (); // simpleType
726                 }
727
728                 private void WriteQName (XmlQualifiedName name)
729                 {
730                         w.WriteQualifiedName (name.Name, name.Namespace);
731                 }
732
733                 private void CheckNamespace (string prefix, string ns, ListDictionary names, ListDictionary includes)
734                 {
735                         if (ns == String.Empty)
736                                 return;
737                         if (ds.Namespace != ns) {
738                                 if (names [prefix] != ns) {
739                                         for (int i = 1; i < int.MaxValue; i++) {
740                                                 string p = "app" + i;
741                                                 if (names [p] == null) {
742                                                         names.Add (p, ns);
743                                                         HandleExternalNamespace (p, ns, includes);
744                                                         break;
745                                                 }
746                                         }
747                                 }
748                         }
749                 }
750
751                 private void HandleExternalNamespace (string prefix, string ns, ListDictionary includes)
752                 {
753                         if (includes.Contains (ns))
754                                 return; // nothing to do
755                         includes.Add (ns, "_" + prefix + ".xsd");
756                 }
757
758                 private /*static*/ XmlQualifiedName MapType (Type type)
759                 {
760                         switch (Type.GetTypeCode (type)) {
761                                 case TypeCode.String: return XmlConstants.QnString;
762                                 case TypeCode.Int16: return XmlConstants.QnShort;
763                                 case TypeCode.Int32: return XmlConstants.QnInt;
764                                 case TypeCode.Int64: return XmlConstants.QnLong;
765                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
766                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
767                                 //case TypeCode.Char: return XmlConstants.QnChar;
768                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
769                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
770                                 case TypeCode.Double: return XmlConstants.QnDouble;
771                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
772                                 case TypeCode.Single: return XmlConstants.QnFloat;
773                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
774                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
775                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
776                         }
777                         
778                         if (typeof (TimeSpan) == type)
779                                 return XmlConstants.QnDuration;
780                         else if (typeof (System.Uri) == type)
781                                 return XmlConstants.QnUri;
782                         else if (typeof (byte[]) == type)
783                                 return XmlConstants.QnBase64Binary;
784                         else if (typeof (XmlQualifiedName) == type)
785                                 return XmlConstants.QnXmlQualifiedName;
786                         else
787                                 return XmlConstants.QnString;
788                 }
789         }
790 }