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