2006-01-16 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", XmlConvert.EncodeLocalName (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", XmlConvert.EncodeLocalName (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 (XmlConvert.EncodeLocalName (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", XmlConvert.EncodeName (rel.RelationName));
240
241                         w.WriteAttributeString (
242                                         XmlConstants.MsdataPrefix,
243                                         "parent",
244                                         XmlConstants.MsdataNamespace,
245                                         XmlConvert.EncodeLocalName (rel.ParentTable.TableName));
246
247                         w.WriteAttributeString (
248                                         XmlConstants.MsdataPrefix,
249                                         "child",
250                                         XmlConstants.MsdataNamespace,
251                                         XmlConvert.EncodeLocalName (rel.ChildTable.TableName));
252
253                         colnames = String.Empty;
254                         foreach (DataColumn col in rel.ParentColumns)
255                                 colnames += XmlConvert.EncodeLocalName (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 += XmlConvert.EncodeLocalName (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 = uniq.ConstraintName;
324                                 w.WriteAttributeString ("name", XmlConvert.EncodeName (name));
325                         }
326                         // otherwise generate new constraint name for the
327                         // XmlSchemaUnique element.
328                         else {
329                                 name = XmlConvert.EncodeLocalName (uniq.Table.TableName) + "_" + uniq.ConstraintName;
330                                 w.WriteAttributeString ("name", name);
331                                 w.WriteAttributeString (
332                                         XmlConstants.MsdataPrefix,
333                                         XmlConstants.ConstraintName,
334                                         XmlConstants.MsdataNamespace,
335                                         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 + XmlConvert.EncodeLocalName (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 (XmlConvert.EncodeLocalName (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", XmlConvert.EncodeLocalName (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 = XmlConvert.EncodeLocalName (rel.ParentTable.TableName) + "_" + 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 (
408                                         uqConst.ConstraintName, ds.Namespace);
409                                 w.WriteEndAttribute ();
410                         }
411
412                         if (isConstraintOnly)
413                                 w.WriteAttributeString ( XmlConstants.MsdataPrefix,
414                                         XmlConstants.ConstraintOnly,
415                                         XmlConstants.MsdataNamespace,
416                                         "true");
417                         else if (rel.Nested)
418                                 w.WriteAttributeString (
419                                         XmlConstants.MsdataPrefix,
420                                         XmlConstants.IsNested,
421                                         XmlConstants.MsdataNamespace,
422                                         "true");
423
424                         AddExtendedPropertyAttributes (uqConst.ExtendedProperties);
425
426                         w.WriteStartElement ("xs", "selector", xmlnsxs);
427                         w.WriteAttributeString ("xpath", ".//" + 
428                                 ConstraintPrefix + XmlConvert.EncodeLocalName (rel.ChildTable.TableName));
429                         w.WriteEndElement ();
430
431                         foreach (DataColumn c in rel.ChildColumns) {
432                                 w.WriteStartElement ("xs", "field",
433                                         xmlnsxs);
434                                 w.WriteStartAttribute ("xpath", String.Empty);
435                                 if (c.ColumnMapping == MappingType.Attribute)
436                                         w.WriteString ("@");
437                                 w.WriteString (ConstraintPrefix);
438                                 w.WriteString (XmlConvert.EncodeLocalName (c.ColumnName));
439                                 w.WriteEndAttribute ();
440                                 w.WriteEndElement (); // field
441                         }
442
443                         w.WriteEndElement (); // keyref
444                 }
445
446                 // ExtendedProperties
447
448                 private bool CheckExtendedPropertyExists (
449                         DataTableCollection tables,
450                         DataRelationCollection relations)
451                 {
452                         if (ds.ExtendedProperties.Count > 0)
453                                 return true;
454                         foreach (DataTable dt in tables) {
455                                 if (dt.ExtendedProperties.Count > 0)
456                                         return true;
457                                 foreach (DataColumn col in dt.Columns)
458                                         if (col.ExtendedProperties.Count > 0)
459                                                 return true;
460                                 foreach (Constraint c in dt.Constraints)
461                                         if (c.ExtendedProperties.Count > 0)
462                                                 return true;
463                         }
464                         if (relations == null)
465                                 return false;
466                         foreach (DataRelation rel in relations)
467                                 if (rel.ExtendedProperties.Count > 0)
468                                         return true;
469                         return false;
470                 }
471
472                 private void AddExtendedPropertyAttributes (PropertyCollection props)
473                 {
474                         // add extended properties to xs:element
475                         foreach (DictionaryEntry de in props) {
476                                 w.WriteStartAttribute (
477                                         XmlConstants.MspropPrefix,
478                                         XmlConvert.EncodeName (de.Key.ToString ()),
479                                         XmlConstants.MspropNamespace);
480                                 if (de.Value != null)
481                                         w.WriteString (
482                                                 DataSet.WriteObjectXml (de.Value));
483                                 w.WriteEndAttribute ();
484                         }
485                 }
486
487                 // Table
488
489                 private void WriteTableElement (DataTable table)
490                 {
491                         w.WriteStartElement ("xs", "element", xmlnsxs);
492                         w.WriteAttributeString ("name", XmlConvert.EncodeLocalName (table.TableName));
493
494                         AddExtendedPropertyAttributes (table.ExtendedProperties);
495
496                         WriteTableType (table);
497
498                         w.WriteEndElement ();
499                 }
500
501                 private void WriteTableType (DataTable table)
502                 {
503                         ArrayList elements;
504                         ArrayList atts;
505                         DataColumn simple;
506
507                         DataSet.SplitColumns (table, out atts, out elements, out simple);
508
509                         w.WriteStartElement ("xs", "complexType", xmlnsxs);
510
511                         if (simple != null) {
512                                 w.WriteStartElement ("xs", "simpleContent", xmlnsxs);
513                                 // add column name attribute
514                                 w.WriteAttributeString (
515                                         XmlConstants.MsdataPrefix,
516                                         XmlConvert.EncodeLocalName (XmlConstants.ColumnName),
517                                         XmlConstants.MsdataNamespace,
518                                         XmlConvert.EncodeLocalName (simple.ColumnName));
519
520                                 // add ordinal attribute
521                                 w.WriteAttributeString (
522                                         XmlConstants.MsdataPrefix,
523                                         XmlConstants.Ordinal,
524                                         XmlConstants.MsdataNamespace,
525                                         XmlConvert.ToString (simple.Ordinal));
526
527                                 // add extension
528                                 w.WriteStartElement ("xs", "extension",
529                                         xmlnsxs);
530                                 w.WriteStartAttribute ("base", String.Empty);
531                                 WriteQName (MapType (simple.DataType));
532                                 w.WriteEndAttribute ();
533
534                                 WriteTableAttributes (atts);
535
536                                 w.WriteEndElement ();
537                         } else {
538                                 WriteTableAttributes (atts);
539
540                                 if (elements.Count > 0) {
541                                         w.WriteStartElement ("xs", "sequence",
542                                                 xmlnsxs);
543                                         foreach (DataColumn col in elements)
544                                                 WriteTableTypeParticles (
545                                                         col);
546                                         w.WriteEndElement ();
547                                 }
548
549                                 foreach (DataRelation rel in table.ChildRelations)
550                                         if (rel.Nested)
551                                                 WriteChildRelations (rel);
552                         }
553
554                         w.WriteFullEndElement (); // complexType
555                 }
556
557                 private void WriteTableTypeParticles (DataColumn col)
558                 {
559                         w.WriteStartElement ("xs", "element",
560                                 xmlnsxs);
561                         w.WriteAttributeString ("name",
562                                 XmlConvert.EncodeLocalName (col.ColumnName));
563
564                         if (col.ColumnName != col.Caption && col.Caption != String.Empty)
565                                 w.WriteAttributeString (
566                                         XmlConstants.MsdataPrefix,
567                                         XmlConstants.Caption, 
568                                         XmlConstants.MsdataNamespace,
569                                         col.Caption);
570
571                         if (col.AutoIncrement == true)
572                                 w.WriteAttributeString (
573                                         XmlConstants.MsdataPrefix,
574                                         XmlConstants.AutoIncrement,
575                                         XmlConstants.MsdataNamespace,
576                                         "true");
577
578                         if (col.AutoIncrementSeed != 0) {
579                                 w.WriteAttributeString (
580
581                                         XmlConstants.MsdataPrefix,
582                                         XmlConstants.AutoIncrementSeed,
583                                         XmlConstants.MsdataNamespace,
584                                         XmlConvert.ToString (col.AutoIncrementSeed));
585                         }
586
587                         if (col.AutoIncrementStep != 1) {
588                                 w.WriteAttributeString (
589
590                                         XmlConstants.MsdataPrefix,
591                                         XmlConstants.AutoIncrementStep,
592                                         XmlConstants.MsdataNamespace,
593                                         XmlConvert.ToString (col.AutoIncrementStep));
594                         }
595
596                         if (col.DefaultValue.ToString () != String.Empty)
597                                 w.WriteAttributeString ("default",
598                                         DataSet.WriteObjectXml (col.DefaultValue));
599
600                         if (col.ReadOnly)
601                                 w.WriteAttributeString (
602                                         XmlConstants.MsdataPrefix,
603                                         XmlConstants.ReadOnly,
604                                         XmlConstants.MsdataNamespace,
605                                         "true");
606
607                         XmlQualifiedName typeQName = null;
608                         if (col.MaxLength < 0) {
609                                 w.WriteStartAttribute ("type", String.Empty);
610                                 typeQName = MapType (col.DataType);
611                                 WriteQName (typeQName);
612                                 w.WriteEndAttribute ();
613                         }
614
615                         if (typeQName == XmlConstants.QnString
616                                 && col.DataType != typeof (string)
617                                 && col.DataType != typeof (char)) {
618                                 w.WriteStartAttribute (
619                                         XmlConstants.MsdataPrefix,
620                                         XmlConstants.DataType,
621                                         XmlConstants.MsdataNamespace);
622                                 string runtimeName = col.DataType.AssemblyQualifiedName;
623                                 w.WriteString (runtimeName);
624                                 w.WriteEndAttribute ();
625                         }
626
627                         if (col.AllowDBNull)
628                                 w.WriteAttributeString ("minOccurs", "0");
629
630                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
631                         //                            XmlConstants.Ordinal, 
632                         //                            XmlConstants.MsdataNamespace, 
633                         //                            col.Ordinal.ToString ());
634
635                         // Write SimpleType if column have MaxLength
636                         if (col.MaxLength > -1)
637                                 WriteSimpleType (col);
638
639                         AddExtendedPropertyAttributes (col.ExtendedProperties);
640
641                         w.WriteEndElement (); // sequence
642                 }
643
644                 private void WriteChildRelations (DataRelation rel)
645                 {
646                         if (rel.ChildTable.Namespace != ds.Namespace) {
647                                 w.WriteStartElement ("xs", "element", xmlnsxs);
648                                 w.WriteStartAttribute ("ref", String.Empty);
649                                 w.WriteQualifiedName (
650                                         XmlConvert.EncodeLocalName (rel.ChildTable.TableName),
651                                         rel.ChildTable.Namespace);
652                                 w.WriteEndAttribute ();
653                         } else {
654                                 w.WriteStartElement ("xs", "element", xmlnsxs);
655                                 w.WriteStartAttribute ("type", String.Empty);
656                                 w.WriteQualifiedName (
657                                         XmlConvert.EncodeLocalName (rel.ChildTable.TableName),
658                                         rel.ChildTable.Namespace);
659                                 w.WriteEndAttribute ();
660                                 w.WriteEndElement ();
661
662                                 globalTypeTables.Add (rel.ChildTable);
663                                 w.WriteAttributeString ("minOccurs", "0");
664                                 w.WriteAttributeString ("maxOccurs", "unbounded");
665                         }
666                 }
667
668                 private void WriteTableAttributes (ArrayList atts)
669                 {
670                         //Then a list of attributes
671                         foreach (DataColumn col in atts) {
672                                 w.WriteStartElement ("xs", "attribute", xmlnsxs);
673
674                                 string name = XmlConvert.EncodeLocalName (col.ColumnName);
675                                 if (col.Namespace != String.Empty) {
676                                         w.WriteAttributeString ("form", "qualified");
677                                         string prefix = col.Prefix == String.Empty ? "app" + additionalNamespaces.Count : col.Prefix;
678                                         name = prefix + ":" + name;
679                                         // FIXME: Handle prefix mapping correctly.
680                                         additionalNamespaces [prefix] = col.Namespace;
681                                 }
682                                 w.WriteAttributeString ("name", name);
683
684                                 AddExtendedPropertyAttributes (
685                                         col.ExtendedProperties);
686
687                                 if (col.ReadOnly)
688                                         w.WriteAttributeString (
689                                                 XmlConstants.MsdataPrefix,
690                                                 XmlConstants.ReadOnly,
691                                                 XmlConstants.MsdataNamespace,
692                                                 "true");
693
694                                 if (col.MaxLength < 0) {
695                                         // otherwise simpleType is written later
696                                         w.WriteStartAttribute ("type", String.Empty);
697                                         WriteQName (MapType (col.DataType));
698                                         w.WriteEndAttribute ();
699                                 }
700
701                                 if (!col.AllowDBNull)
702                                         w.WriteAttributeString ("use", "required");
703                                 if (col.DefaultValue.ToString () != String.Empty)
704                                         w.WriteAttributeString ("default",
705                                                 DataSet.WriteObjectXml (col.DefaultValue));
706
707                                 if (col.MaxLength > -1)
708                                         WriteSimpleType (col);
709
710                                 w.WriteEndElement (); // attribute
711                         }
712                 }
713
714                 private void WriteSimpleType (DataColumn col)
715                 {
716                         w.WriteStartElement ("xs", "simpleType", xmlnsxs);
717                         w.WriteStartElement ("xs", "restriction", xmlnsxs);
718                         w.WriteStartAttribute ("base", String.Empty);
719                         WriteQName (MapType (col.DataType));
720                         w.WriteEndAttribute ();
721
722                         w.WriteStartElement ("xs", "maxLength", xmlnsxs);
723                         w.WriteAttributeString ("value",
724                                 XmlConvert.ToString (col.MaxLength));
725                         w.WriteEndElement (); // maxLength
726                         w.WriteEndElement (); // restriction
727                         w.WriteEndElement (); // simpleType
728                 }
729
730                 private void WriteQName (XmlQualifiedName name)
731                 {
732                         w.WriteQualifiedName (name.Name, name.Namespace);
733                 }
734
735                 private void CheckNamespace (string prefix, string ns, ListDictionary names, ListDictionary includes)
736                 {
737                         if (ns == String.Empty)
738                                 return;
739                         if (ds.Namespace != ns) {
740                                 if (names [prefix] != ns) {
741                                         for (int i = 1; i < int.MaxValue; i++) {
742                                                 string p = "app" + i;
743                                                 if (names [p] == null) {
744                                                         names.Add (p, ns);
745                                                         HandleExternalNamespace (p, ns, includes);
746                                                         break;
747                                                 }
748                                         }
749                                 }
750                         }
751                 }
752
753                 private void HandleExternalNamespace (string prefix, string ns, ListDictionary includes)
754                 {
755                         if (includes.Contains (ns))
756                                 return; // nothing to do
757                         includes.Add (ns, "_" + prefix + ".xsd");
758                 }
759
760                 private /*static*/ XmlQualifiedName MapType (Type type)
761                 {
762                         switch (Type.GetTypeCode (type)) {
763                                 case TypeCode.String: return XmlConstants.QnString;
764                                 case TypeCode.Int16: return XmlConstants.QnShort;
765                                 case TypeCode.Int32: return XmlConstants.QnInt;
766                                 case TypeCode.Int64: return XmlConstants.QnLong;
767                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
768                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
769                                 //case TypeCode.Char: return XmlConstants.QnChar;
770                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
771                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
772                                 case TypeCode.Double: return XmlConstants.QnDouble;
773                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
774                                 case TypeCode.Single: return XmlConstants.QnFloat;
775                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
776                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
777                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
778                         }
779                         
780                         if (typeof (TimeSpan) == type)
781                                 return XmlConstants.QnDuration;
782                         else if (typeof (System.Uri) == type)
783                                 return XmlConstants.QnUri;
784                         else if (typeof (byte[]) == type)
785                                 return XmlConstants.QnBase64Binary;
786                         else if (typeof (XmlQualifiedName) == type)
787                                 return XmlConstants.QnXmlQualifiedName;
788                         else
789                                 return XmlConstants.QnString;
790                 }
791         }
792 }