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