2010-01-20 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / System.Data / System.Data / XmlSchemaDataImporter.cs
1 //
2 // XmlSchemaDataImporter.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // (C)2004 Novell Inc.
8 //
9 //
10 // ***** The design note became somewhat obsolete. Should be rewritten. *****
11 //
12 // * Design Notes
13 //
14 // ** Abstract
15 //
16 //      This class is used to import an XML Schema into a DataSet schema.
17 //
18 //      Only XmlReader is acceptable as the input to the class.
19 //      This class is not expected to read XML Schema multi time.
20 //
21 // ** Targetable Schema Components
22 //
23 //      Only global global elements that hold complex type are converted 
24 //      into a table. 
25 //      <del>
26 //      The components of the type of the element are subsequently converted
27 //      into a table, BUT there is an exception. As for "DataSet elements",
28 //      the type is just ignored (see "DataSet Element definition" below).
29 //      </del><ins>
30 //      The components of the type of the element are subsequently converted
31 //      into a table. As for "DataSet elements", its complex type is also 
32 //      handled.
33 //      </ins>
34 //
35 //      Unused complex types are never be converted.
36 //
37 //      Global simple types and global attributes are never converted.
38 //      They cannot be a table.
39 //      Local complex types are also converted into a table.
40 //
41 //      Local elements are converted into either a table or a column in
42 //      the "context DataTable". Simple-typed element is not always converted
43 //      into a DataColumn; if maxOccurs > 1, it will be converted as a table.
44 //
45 // ** Name Convention
46 //
47 //      Ignore this section. Microsoft.NET was buggy enough to confuse
48 //      against these name conflicts.
49 //
50 //      Since local complex types are anonymous, we have to name for each
51 //      component. Thus, and since complex types and elements can have the 
52 //      same name each other, we have to manage a table for mappings from 
53 //      a name to a component. The names must be also used in DataRelation
54 //      definitions correctly.
55 //
56 // ** DataSet element definition
57 //
58 //      "DataSet element" is 1) such element that has an attribute 
59 //      msdata:IsDataSet (where prefix "msdata" is bound to 
60 //      urn:schemas-microsoft-com:xml-msdata), or 2) the only one
61 //      element definition in the schema.
62 //
63 //      There is another complicated rule. 1) If there is only one element EL 
64 //      in the schema, and 2) if the type of EL is complex named CT, and 3)
65 //      the content of the CT is a group base, and 4) the group base contains 
66 //      an element EL2, and finally 5) if EL2 is complex, THEN the element is
67 //      the DataSet element.
68 //
69 //      Only the first global element that matches the condition above is
70 //      regarded as DataSet element (by necessary design or just a bug?) 
71 //      instead of handling as an error.
72 //
73 //      All global elements are considered as an alternative in the dataset
74 //      element.
75 //
76 //      For local elements, msdata:IsDataSet are just ignored.
77 //
78 // ** Importing Complex Types as Columns
79 //
80 //      When an xs:element is going to be mapped, its complex type (remember
81 //      that only complex-typed elements are targettable) are expanded to
82 //      DataColumn.
83 //
84 //      DataColumn has a property MappingType that shows whether this column
85 //       came from attribute or element.
86 //
87 //      [Question: How about MappingType.Simple? How is it used?]
88 //
89 //      Additionally, for particle elements, it might also create another
90 //      DataTable (but for the particle elements in context DataTable, it
91 //      will create an index to the new table).
92 //
93 //      For group base particles (XmlSchemaGroupBase; sequence, choice, all)
94 //      each component in those groups are mapped to a column. Even if you
95 //      import "choice" or "all" components, DataSet.WriteXmlSchema() will
96 //      output them just as a "sequence".
97 //
98 //      Columns cannot be added directly to current context DataTable; they
99 //      need to be added after processing all the columns, because they may
100 //      have msdata:Ordinal attribute that specifies the order of the columns
101 //      in the DataTable.
102 //
103 //      "Nested elements" are not allowed. (Clarification required?)
104 //
105 // ** Identity Constraints and DataRelations
106 //
107 // *** DataRelations from element identity constraints
108 //
109 //      Only constraints on "DataSet element" is considered. All other
110 //      constraint definitions are ignored. Note that it is DataSet that has
111 //      the property Relations (of type DataRelationCollection).
112 //
113 //      xs:key and xs:unique are handled as the same (then both will be
114 //      serialized as xs:unique).
115 //
116 //      The XPath expressions in the constraints are strictly limited; they
117 //      are expected to be expandable enough to be mappable for each
118 //
119 //              * selector to "any_valid_XPath/is/OK/blah" 
120 //                where "blah" is one of the DataTable name. It looks that
121 //                only the last QName section is significant and any heading
122 //                XPath step is OK (even if the mapped node does not exist).
123 //              * field to QName that is mapped to DataColumn in the DataTable
124 //                (even ./QName is not allowed)
125 //
126 // *** DataRelations from annotations
127 //
128 //      See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_mapping_relationship_specified_for_nested_elements.asp and http://msdn.microsoft.com/library/en-us/cpguide/html/_specifying_relationship_between_elements_with_no_nesting.asp
129 //
130 // ** Informative References
131 //
132 // Generating DataSet Relational Structure from XML Schema (XSD)
133 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_generating_dataset_relational_structure_from_xsd.asp
134 //
135
136 //
137 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
138 //
139 // Permission is hereby granted, free of charge, to any person obtaining
140 // a copy of this software and associated documentation files (the
141 // "Software"), to deal in the Software without restriction, including
142 // without limitation the rights to use, copy, modify, merge, publish,
143 // distribute, sublicense, and/or sell copies of the Software, and to
144 // permit persons to whom the Software is furnished to do so, subject to
145 // the following conditions:
146 // 
147 // The above copyright notice and this permission notice shall be
148 // included in all copies or substantial portions of the Software.
149 // 
150 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
151 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
152 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
153 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
154 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
155 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
156 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
157 //
158
159 using System;
160 using System.Collections;
161 using System.Data;
162 using System.Data.Common;
163 using System.Globalization;
164 using System.Xml;
165 using System.Xml.Schema;
166
167
168 namespace System.Data
169 {
170         internal class TableStructureCollection : CollectionBase
171         {
172                 public void Add (TableStructure table)
173                 {
174                         List.Add (table);
175                 }
176
177                 public TableStructure this [int i] {
178                         get { return List [i] as TableStructure; }
179                 }
180
181                 public TableStructure this [string name] {
182                         get {
183                                 foreach (TableStructure ts in List)
184                                         if (ts.Table.TableName == name)
185                                                 return ts;
186                                 return null;
187                         }
188                 }
189         }
190
191         internal class RelationStructureCollection : CollectionBase
192         {
193                 public void Add (RelationStructure rel)
194                 {
195                         List.Add (rel);
196                 }
197
198                 public RelationStructure this [int i] {
199                         get { return List [i] as RelationStructure; }
200                 }
201
202                 public RelationStructure this [string parent, string child] {
203                         get {
204                                 foreach (RelationStructure rel in List)
205                                         if (rel.ParentTableName == parent && rel.ChildTableName == child)
206                                                 return rel;
207                                 return null;
208                         }
209                 }
210         }
211         
212         internal class TableStructure
213         {
214                 public TableStructure (DataTable table)
215                 {
216                         this.Table = table;
217                 }
218
219                 // The columns and orders which will be added to the context
220                 // table (See design notes; Because of the ordinal problem)
221                 public DataTable Table;
222                 public Hashtable OrdinalColumns = new Hashtable ();
223                 public ArrayList NonOrdinalColumns = new ArrayList ();
224                 public DataColumn PrimaryKey;
225
226                 public bool ContainsColumn (string name)
227                 {
228                         foreach (DataColumn col in NonOrdinalColumns)
229                                 if (col.ColumnName == name)
230                                         return true;
231                         foreach (DataColumn col in OrdinalColumns.Keys)
232                                 if (col.ColumnName == name)
233                                         return true;
234                         return false;
235                 }
236         }
237
238         internal class RelationStructure
239         {
240                 public string ExplicitName;
241                 public string ParentTableName;
242                 public string ChildTableName;
243                 public string ParentColumnName;
244                 public string ChildColumnName;
245                 public bool IsNested;
246                 public bool CreateConstraint;
247         }
248
249         internal class ConstraintStructure
250         {
251                 public readonly string TableName;
252                 public readonly string [] Columns;
253                 public readonly bool [] IsAttribute;
254                 public readonly string ConstraintName;
255                 public readonly bool IsPrimaryKey;
256                 public readonly string ReferName;
257                 public readonly bool IsNested;
258                 public readonly bool IsConstraintOnly;
259
260                 public ConstraintStructure (string tname, string [] cols, bool [] isAttr, string cname, bool isPK, string refName, bool isNested, bool isConstraintOnly)
261                 {
262                         TableName = tname;
263                         Columns = cols;
264                         IsAttribute = isAttr;
265                         ConstraintName = XmlHelper.Decode (cname);
266                         IsPrimaryKey = isPK;
267                         ReferName = refName;
268                         IsNested = isNested;
269                         IsConstraintOnly = isConstraintOnly;
270                 }
271         }
272
273         internal class XmlSchemaDataImporter
274         {
275                 static readonly XmlSchemaDatatype schemaIntegerType;
276                 static readonly XmlSchemaDatatype schemaDecimalType;
277                 static readonly XmlSchemaComplexType schemaAnyType;
278
279                 static XmlSchemaDataImporter ()
280                 {
281                         XmlSchema s = new XmlSchema ();
282                         XmlSchemaAttribute a = new XmlSchemaAttribute ();
283                         a.Name = "foo";
284                         a.SchemaTypeName = new XmlQualifiedName ("integer", XmlSchema.Namespace);
285                         s.Items.Add (a);
286                         XmlSchemaAttribute b = new XmlSchemaAttribute ();
287                         b.Name = "bar";
288                         b.SchemaTypeName = new XmlQualifiedName ("decimal", XmlSchema.Namespace);
289                         s.Items.Add (b);
290                         XmlSchemaElement e = new XmlSchemaElement ();
291                         e.Name = "bar";
292                         s.Items.Add (e);
293                         s.Compile (null);
294 #if NET_2_0
295                         schemaIntegerType = ((XmlSchemaSimpleType) a.AttributeSchemaType).Datatype;
296                         schemaDecimalType = ((XmlSchemaSimpleType) b.AttributeSchemaType).Datatype;
297                         schemaAnyType = e.ElementSchemaType as XmlSchemaComplexType;
298 #else
299                         schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
300                         schemaDecimalType = b.AttributeType as XmlSchemaDatatype;
301                         schemaAnyType = e.ElementType as XmlSchemaComplexType;
302 #endif
303                 }
304
305                 #region Fields
306
307                 DataSet dataset;
308                 bool forDataSet;
309                 XmlSchema schema;
310
311                 ArrayList relations = new ArrayList ();
312                 Hashtable reservedConstraints = new Hashtable ();
313
314                 // such element that has an attribute msdata:IsDataSet="true"
315                 XmlSchemaElement datasetElement;
316
317                 // choice alternatives in the "dataset element"
318                 ArrayList topLevelElements = new ArrayList ();
319
320                 // import target elements
321                 ArrayList targetElements = new ArrayList ();
322
323                 TableStructure currentTable;
324         
325 #if NET_2_0
326                 // TODO: Do we need a collection here?
327                 TableAdapterSchemaInfo currentAdapter;
328 #endif
329                 #endregion
330
331                 // .ctor()
332
333                 public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
334                 {
335                         this.dataset = dataset;
336                         this.forDataSet = forDataSet;
337                         dataset.DataSetName = "NewDataSet"; // Initialize always
338                         schema = XmlSchema.Read (reader, null);
339                         if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace)
340                                 reader.ReadEndElement ();
341                         schema.Compile (null);
342                 }
343
344 #if NET_2_0
345                 // properties
346                 internal TableAdapterSchemaInfo CurrentAdapter {
347                         get { return currentAdapter; }
348                 }
349 #endif
350                 
351                 // methods
352
353                 public void Process ()
354                 {
355                         if (schema.Id != null)
356                                 dataset.DataSetName = schema.Id; // default. Overridable by "DataSet element"
357                         dataset.Namespace = schema.TargetNamespace;
358
359                         // Find dataset element
360                         foreach (XmlSchemaObject obj in schema.Items) {
361                                 XmlSchemaElement el = obj as XmlSchemaElement;
362                                 if (el != null) {
363                                         if (datasetElement == null &&
364                                                 IsDataSetElement (el))
365                                                 datasetElement = el;
366 #if NET_2_0
367                                         if (el.ElementSchemaType is XmlSchemaComplexType &&
368                                             el.ElementSchemaType != schemaAnyType)
369 #else
370                                         if (el.ElementType is XmlSchemaComplexType &&
371                                             el.ElementType != schemaAnyType)
372 #endif
373                                                 targetElements.Add (obj);
374                                 }
375                         }
376
377                         // make reservation of identity constraints
378                         if (datasetElement != null) {
379                                 // keys/uniques.
380                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
381                                         if (! (obj is XmlSchemaKeyref))
382                                                 ReserveSelfIdentity ((XmlSchemaIdentityConstraint) obj);
383                                 // keyrefs.
384                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
385                                         if (obj is XmlSchemaKeyref)
386                                                 ReserveRelationIdentity (datasetElement, (XmlSchemaKeyref) obj);
387                         }
388
389                         foreach (XmlSchemaObject obj in schema.Items) {
390                                 if (obj is XmlSchemaElement) {
391                                         XmlSchemaElement el = obj as XmlSchemaElement;
392 #if NET_2_0
393                                         if (el.ElementSchemaType is XmlSchemaComplexType &&
394                                             el.ElementSchemaType != schemaAnyType)
395 #else
396                                         if (el.ElementType is XmlSchemaComplexType &&
397                                             el.ElementType != schemaAnyType)
398 #endif
399                                                 targetElements.Add (obj);
400                                 }
401                         }
402
403                         // This collection will grow up while processing elements.
404                         int globalElementCount = targetElements.Count;
405
406                         for (int i = 0; i < globalElementCount; i++)
407                                 ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
408
409                         // Rest are local elements.
410                         for (int i = globalElementCount; i < targetElements.Count; i++)
411                                 ProcessDataTableElement ((XmlSchemaElement) targetElements [i]);
412
413                         // Handle relation definitions written as xs:annotation.
414                         // See detail: http://msdn.microsoft.com/library/shared/happyUrl/fnf_msdn.asp?Redirect=%22http://msdn.microsoft.com/404/default.asp%22
415                         foreach (XmlSchemaObject obj in schema.Items)
416                                 if (obj is XmlSchemaAnnotation)
417                                         HandleAnnotations ((XmlSchemaAnnotation) obj, false);
418
419                         if (datasetElement != null) {
420                                 // Handle constraints in the DataSet element. First keys.
421                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
422                                         if (! (obj is XmlSchemaKeyref))
423                                                 ProcessSelfIdentity (reservedConstraints [obj] as ConstraintStructure);
424                                 // Then keyrefs.
425                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
426                                         if (obj is XmlSchemaKeyref)
427                                                 ProcessRelationIdentity (datasetElement, reservedConstraints [obj] as ConstraintStructure);
428                         }
429
430                         foreach (RelationStructure rs in this.relations)
431                                 dataset.Relations.Add (GenerateRelationship (rs));
432                 }
433
434                 private bool IsDataSetElement (XmlSchemaElement el)
435                 {
436                         if (el.UnhandledAttributes != null) {
437                                 foreach (XmlAttribute attr in el.UnhandledAttributes) {
438                                         if (attr.LocalName == "IsDataSet" &&
439                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace) {
440                                                 switch (attr.Value) {
441                                                 case "true": // case sensitive
442                                                         return true;
443                                                 case "false":
444                                                         break;
445                                                 default:
446                                                         throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
447                                                 }
448                                         }
449                                 }
450                         }
451
452                         if (schema.Elements.Count != 1)
453                                 return false;
454                         if (!(el.SchemaType is XmlSchemaComplexType))
455                                 return false;
456                         XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
457                         if (ct.AttributeUses.Count > 0)
458                                 return false;
459                         XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
460                         if (gb == null || gb.Items.Count == 0)
461                                 return false;
462                         foreach (XmlSchemaParticle p in gb.Items) {
463                                 if (ContainsColumn (p))
464                                         return false;
465                         }
466                         return true;
467                 }
468
469                 private bool ContainsColumn (XmlSchemaParticle p)
470                 {
471                         XmlSchemaElement el = p as XmlSchemaElement;
472                         if (el != null) {
473                                 XmlSchemaComplexType ct = null;
474 #if NET_2_0
475                                 ct = el.ElementSchemaType as XmlSchemaComplexType;
476 #else
477                                 ct = el.ElementType as XmlSchemaComplexType;
478 #endif
479                                 if (ct == null || ct == schemaAnyType)
480                                         return true; // column element
481                                 if (ct.AttributeUses.Count > 0)
482                                         return false; // table element
483                                 if (ct.ContentType == XmlSchemaContentType.TextOnly)
484                                         return true; // column element
485                                 else
486                                         return false; // table element
487                         }
488                         XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
489                         for (int i = 0; i < gb.Items.Count; i++) {
490                                 if (ContainsColumn ((XmlSchemaParticle) gb.Items [i]))
491                                         return true;
492                         }
493                         return false;
494                 }
495
496                 private void ProcessGlobalElement (XmlSchemaElement el)
497                 {
498                         // If it is already registered (by resolving reference
499                         // in previously-imported elements), just ignore.
500                         if (dataset.Tables.Contains (el.QualifiedName.Name))
501                                 return;
502
503                         // If type is not complex, just skip this element
504 #if NET_2_0
505                         if (! (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType))
506 #else
507                         if (! (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType))
508 #endif
509                                 return;
510
511                         if (IsDataSetElement (el)) {
512                                 ProcessDataSetElement (el);
513                                 return;
514                         }
515                         else
516                                 dataset.Locale = CultureInfo.CurrentCulture;
517
518                         // Register as a top-level element
519                         topLevelElements.Add (el);
520                         // Create DataTable for this element
521                         ProcessDataTableElement (el);
522                 }
523
524                 private void ProcessDataSetElement (XmlSchemaElement el)
525                 {
526                         dataset.DataSetName = el.Name;
527                         this.datasetElement = el;
528
529                         // Search for locale attributes
530                         bool useCurrent = false;
531                         if (el.UnhandledAttributes != null) {
532                                 foreach (XmlAttribute attr in el.UnhandledAttributes) {
533 #if NET_2_0
534                                         if (attr.LocalName == "UseCurrentLocale" &&
535                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace)
536                                                 useCurrent = true;
537 #endif
538                                         if (attr.LocalName == "Locale" &&
539                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace) {
540                                                 CultureInfo ci = new CultureInfo (attr.Value);
541                                                 dataset.Locale = ci;
542                                         }
543                                 }
544                         }
545 #if NET_2_0
546                         if (!useCurrent && !dataset.LocaleSpecified) // then set current culture instance _explicitly_
547                                 dataset.Locale = CultureInfo.CurrentCulture;
548 #endif
549
550                         // Process content type particle (and create DataTable)
551                         XmlSchemaComplexType ct = null;
552 #if NET_2_0
553                         ct = el.ElementSchemaType as XmlSchemaComplexType;
554 #else
555                         ct = el.ElementType as XmlSchemaComplexType;
556 #endif
557                         XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
558                         if (p != null)
559                                 HandleDataSetContentTypeParticle (p);
560                 }
561
562                 private void HandleDataSetContentTypeParticle (XmlSchemaParticle p)
563                 {
564                         XmlSchemaElement el = p as XmlSchemaElement;
565                         if (el != null) {
566 #if NET_2_0
567                                 if (el.ElementSchemaType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
568 #else
569                                 if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
570 #endif
571                                         ProcessDataTableElement (el);
572                         }
573                         else if (p is XmlSchemaGroupBase) {
574                                 foreach (XmlSchemaParticle pc in ((XmlSchemaGroupBase) p).Items)
575                                         HandleDataSetContentTypeParticle (pc);
576                         }
577                 }
578
579                 private void ProcessDataTableElement (XmlSchemaElement el)
580                 {
581                         string tableName = XmlHelper.Decode (el.QualifiedName.Name);
582                         // If it is already registered, just ignore.
583                         if (dataset.Tables.Contains (tableName))
584                                 return;
585
586                         DataTable table = new DataTable (tableName);
587                         table.Namespace = el.QualifiedName.Namespace;
588                         TableStructure oldTable = currentTable;
589                         currentTable = new TableStructure (table);
590
591                         dataset.Tables.Add (table);
592
593                         // Find Locale
594                         if (el.UnhandledAttributes != null) {
595                                 foreach (XmlAttribute attr in el.UnhandledAttributes) {
596                                         if (attr.LocalName == "Locale" &&
597                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace)
598                                                 table.Locale = new CultureInfo (attr.Value);
599                                 }
600                         }
601
602                         // Handle complex type (NOTE: It is (or should be)
603                         // impossible the type is other than complex type).
604                         XmlSchemaComplexType ct = null;
605 #if NET_2_0
606                         ct = (XmlSchemaComplexType) el.ElementSchemaType;
607 #else
608                         ct = (XmlSchemaComplexType) el.ElementType;
609 #endif
610
611                         // Handle attributes
612                         foreach (DictionaryEntry de in ct.AttributeUses)
613                                 ImportColumnAttribute ((XmlSchemaAttribute) de.Value);
614
615                         // Handle content type particle
616                         if (ct.ContentTypeParticle is XmlSchemaElement)
617                                 ImportColumnElement (el, (XmlSchemaElement) ct.ContentTypeParticle);
618                         else if (ct.ContentTypeParticle is XmlSchemaGroupBase)
619                                 ImportColumnGroupBase (el, (XmlSchemaGroupBase) ct.ContentTypeParticle);
620                         // else if null then do nothing.
621
622                         // Handle simple content
623                         switch (ct.ContentType) {
624                         case XmlSchemaContentType.TextOnly:
625 //                      case XmlSchemaContentType.Mixed:
626                                 // LAMESPEC: When reading from XML Schema, it maps to "_text", while on the data inference, it is mapped to "_Text" (case ignorant).
627                                 string simpleName = el.QualifiedName.Name + "_text";
628                                 DataColumn simple = new DataColumn (simpleName);
629                                 simple.Namespace = el.QualifiedName.Namespace;
630                                 simple.AllowDBNull = (el.MinOccurs == 0);
631                                 simple.ColumnMapping = MappingType.SimpleContent;
632                                 simple.DataType = ConvertDatatype (ct.Datatype);
633                                 currentTable.NonOrdinalColumns.Add (simple);
634                                 break;
635                         }
636
637                         // add columns to the table in specified order 
638                         // (by msdata:Ordinal attributes)
639                         SortedList sd = new SortedList ();
640                         foreach (DictionaryEntry de in currentTable.OrdinalColumns)
641                                 sd.Add (de.Value, de.Key);
642                         foreach (DictionaryEntry de in sd)
643                                 table.Columns.Add ((DataColumn) de.Value);
644                         foreach (DataColumn dc in currentTable.NonOrdinalColumns)
645                                 table.Columns.Add (dc);
646
647                         currentTable = oldTable;
648                 }
649
650                 private DataRelation GenerateRelationship (RelationStructure rs)
651                 {
652                         DataTable ptab = dataset.Tables [rs.ParentTableName];
653                         DataTable ctab = dataset.Tables [rs.ChildTableName];
654
655                         DataRelation rel ;
656                         string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
657
658                         // Annotation Relations belonging to a DataSet can contain multiple colnames
659                         // in parentkey and childkey.
660                         if (datasetElement != null) {
661                                 String[] pcolnames = rs.ParentColumnName.Split (null);
662                                 String[] ccolnames = rs.ChildColumnName.Split (null);
663
664                                 DataColumn[] pcol = new DataColumn [pcolnames.Length];
665                                 for (int i=0; i<pcol.Length; ++i)
666                                         pcol [i] = ptab.Columns [XmlHelper.Decode (pcolnames [i])];
667
668                                 DataColumn[] ccol = new DataColumn [ccolnames.Length];
669                                 for (int i=0; i < ccol.Length; ++i) {
670                                         ccol [i] = ctab.Columns [XmlHelper.Decode (ccolnames [i])];
671                                         if (ccol [i] == null)
672                                                 ccol [i] = CreateChildColumn (pcol [i], ctab);
673                                 }
674                                 rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
675                         } else {
676                                 DataColumn pcol = ptab.Columns [XmlHelper.Decode (rs.ParentColumnName)];
677                                 DataColumn ccol = ctab.Columns [XmlHelper.Decode (rs.ChildColumnName)];
678                                 if (ccol == null) 
679                                         ccol = CreateChildColumn (pcol, ctab);
680                                 rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
681                         }
682                         rel.Nested = rs.IsNested;
683                         if (rs.CreateConstraint)
684                                 rel.ParentTable.PrimaryKey = rel.ParentColumns;
685                         return rel;
686                 }
687
688                 private DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
689                 {
690                         DataColumn col = childTable.Columns.Add (parentColumn.ColumnName, 
691                                                                 parentColumn.DataType);
692                         col.Namespace = String.Empty;
693                         col.ColumnMapping = MappingType.Hidden;
694                         return col;
695                 }
696
697                 private void ImportColumnGroupBase (XmlSchemaElement parent, XmlSchemaGroupBase gb)
698                 {
699                         foreach (XmlSchemaParticle p in gb.Items) {
700                                 XmlSchemaElement el = p as XmlSchemaElement;
701                                 if (el != null)
702                                         ImportColumnElement (parent, el);
703                                 else if (p is XmlSchemaGroupBase)
704                                         ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
705                                 // otherwise p is xs:any
706                         }
707                 }
708
709                 private XmlSchemaDatatype GetSchemaPrimitiveType (object type)
710                 {
711                         if (type is XmlSchemaComplexType)
712                                 return null; // It came here, so that maybe it is xs:anyType
713                         XmlSchemaDatatype dt = type as XmlSchemaDatatype;
714                         if (dt == null && type != null)
715                                 dt = ((XmlSchemaSimpleType) type).Datatype;
716                         return dt;
717                 }
718
719                 // Note that this column might be Hidden
720                 private void ImportColumnAttribute (XmlSchemaAttribute attr)
721                 {
722                         DataColumn col = new DataColumn ();
723                         col.ColumnName = attr.QualifiedName.Name;
724                         col.Namespace = attr.QualifiedName.Namespace;
725                         XmlSchemaDatatype dt = null;
726 #if NET_2_0
727                         dt = GetSchemaPrimitiveType (((XmlSchemaSimpleType) attr.AttributeSchemaType).Datatype);
728 #else
729                         dt = GetSchemaPrimitiveType (attr.AttributeType);
730 #endif
731                         // This complicated check comes from the fact that
732                         // MS.NET fails to map System.Object to anyType (that
733                         // will cause ReadTypedObject() fail on XmlValidatingReader).
734                         // ONLY In DataSet context, we set System.String for
735                         // simple ur-type.
736                         col.DataType = ConvertDatatype (dt);
737                         if (col.DataType == typeof (object))
738                                 col.DataType = typeof (string);
739                         // When attribute use="prohibited", then it is regarded as 
740                         // Hidden column.
741                         if (attr.Use == XmlSchemaUse.Prohibited)
742                                 col.ColumnMapping = MappingType.Hidden;
743                         else {
744                                 col.ColumnMapping = MappingType.Attribute;
745                                 col.DefaultValue = GetAttributeDefaultValue (attr);
746                         }
747                         if (attr.Use == XmlSchemaUse.Required)
748                                 col.AllowDBNull = false;
749
750 #if NET_2_0
751                         FillFacet (col, attr.AttributeSchemaType as XmlSchemaSimpleType);
752 #else
753                         FillFacet (col, attr.AttributeType as XmlSchemaSimpleType);
754 #endif
755
756                         // Call this method after filling the name
757                         ImportColumnMetaInfo (attr, attr.QualifiedName, col);
758                         AddColumn (col);
759                 }
760
761                 private void ImportColumnElement (XmlSchemaElement parent, XmlSchemaElement el)
762                 {
763                         // FIXME: element nest check
764
765                         DataColumn col = new DataColumn ();
766                         col.DefaultValue = GetElementDefaultValue (el);
767                         col.AllowDBNull = (el.MinOccurs == 0);
768
769 #if NET_2_0
770                         if (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType)
771 #else
772                         if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType)
773 #endif
774                                 FillDataColumnComplexElement (parent, el, col);
775                         else if (el.MaxOccurs != 1)
776                                 FillDataColumnRepeatedSimpleElement (parent, el, col);
777                         else
778                                 FillDataColumnSimpleElement (el, col);
779                 }
780
781                 // common process for element and attribute
782                 private void ImportColumnMetaInfo (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
783                 {
784                         if (obj.UnhandledAttributes != null) {
785                                 foreach (XmlAttribute attr in obj.UnhandledAttributes) {
786                                         if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
787                                                 continue;
788                                         switch (attr.LocalName) {
789                                         case XmlConstants.Caption:
790                                                 col.Caption = attr.Value;
791                                                 break;
792                                         case XmlConstants.DataType:
793                                                 col.DataType = Type.GetType (attr.Value);
794                                                 break;
795                                         case XmlConstants.AutoIncrement:
796                                                 col.AutoIncrement = bool.Parse (attr.Value);
797                                                 break;
798                                         case XmlConstants.AutoIncrementSeed:
799                                                 col.AutoIncrementSeed = int.Parse (attr.Value);
800                                                 break;
801                                         case XmlConstants.AutoIncrementStep:
802                                                 col.AutoIncrementStep = int.Parse (attr.Value);
803                                                 break;
804                                         case XmlConstants.ReadOnly:
805                                                 col.ReadOnly = XmlConvert.ToBoolean (attr.Value);
806                                                 break;
807                                         case XmlConstants.Ordinal:
808                                                 int ordinal = int.Parse (attr.Value);
809                                                 break;
810                                         }
811                                 }
812                         }
813                 }
814
815                 private void FillDataColumnComplexElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
816                 {
817                         if (targetElements.Contains (el))
818                                 return; // do nothing
819
820                         string elName = XmlHelper.Decode (el.QualifiedName.Name);
821                         if (elName == dataset.DataSetName)
822                                 // Well, why it is ArgumentException :-?
823                                 throw new ArgumentException ("Nested element must not have the same name as DataSet's name.");
824
825                         if (el.Annotation != null)
826                                 HandleAnnotations (el.Annotation, true);
827                         // If xsd:keyref xsd:key for this table exists, then don't add
828                         // relation here manually.
829                         else if (!DataSetDefinesKey (elName)) {
830                                 AddParentKeyColumn (parent, el, col);
831
832                                 RelationStructure rel = new RelationStructure ();
833                                 rel.ParentTableName = XmlHelper.Decode (parent.QualifiedName.Name);
834                                 rel.ChildTableName = elName;
835                                 rel.ParentColumnName = col.ColumnName;
836                                 rel.ChildColumnName = col.ColumnName;
837                                 rel.CreateConstraint = true;
838                                 rel.IsNested = true;
839                                 relations.Add (rel);
840                         }
841
842                         // If the element is not referenced one, the element will be handled later.
843                         if (el.RefName == XmlQualifiedName.Empty)
844                                 ProcessDataTableElement (el);
845
846                 }
847
848                 private bool DataSetDefinesKey (string name)
849                 {
850                         foreach (ConstraintStructure c in reservedConstraints.Values)
851                                 if (c.TableName == name && (c.IsPrimaryKey || c.IsNested))
852                                         return true;
853                         return false;
854                 }
855
856                 private void AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
857                 {
858                         // check existing primary key
859                         if (currentTable.Table.PrimaryKey.Length > 0)
860                                 throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", currentTable.Table.TableName));
861
862                         if (currentTable.PrimaryKey != null) {
863                                 // fill pk column info and return
864                                 col.ColumnName = currentTable.PrimaryKey.ColumnName;
865                                 col.ColumnMapping = currentTable.PrimaryKey.ColumnMapping;
866                                 col.Namespace = currentTable.PrimaryKey.Namespace;
867                                 col.DataType = currentTable.PrimaryKey.DataType;
868                                 col.AutoIncrement = currentTable.PrimaryKey.AutoIncrement;
869                                 col.AllowDBNull = currentTable.PrimaryKey.AllowDBNull;
870                                 
871                                 ImportColumnMetaInfo (el, el.QualifiedName, col);
872                                 return;
873                         }
874
875                         // check name identity
876                         string name = XmlHelper.Decode (parent.QualifiedName.Name) + "_Id";
877                         int count = 0;
878                         while (currentTable.ContainsColumn (name))
879                                 name = String.Format ("{0}_{1}", name, count++);
880
881                         col.ColumnName = name;
882                         col.ColumnMapping = MappingType.Hidden;
883                         col.Namespace = parent.QualifiedName.Namespace;
884                         col.DataType = typeof (int);
885                         col.AutoIncrement = true;
886                         col.AllowDBNull = false;
887                         ImportColumnMetaInfo (el, el.QualifiedName, col);
888                         
889                         AddColumn (col);
890                         currentTable.PrimaryKey = col;
891                 }
892
893                 private void FillDataColumnRepeatedSimpleElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
894                 {
895                         if (targetElements.Contains (el))
896                                 return; // do nothing
897
898                         AddParentKeyColumn (parent, el, col);
899                         DataColumn pkey = currentTable.PrimaryKey;
900
901                         string elName = XmlHelper.Decode (el.QualifiedName.Name);
902                         string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
903
904                         DataTable dt = new DataTable ();
905                         dt.TableName = elName;
906                         dt.Namespace = el.QualifiedName.Namespace;
907                         // reference key column to parent
908                         DataColumn cc = new DataColumn ();
909                         cc.ColumnName = parentName + "_Id";
910                         cc.Namespace = parent.QualifiedName.Namespace;
911                         cc.ColumnMapping = MappingType.Hidden;
912                         cc.DataType = typeof (int);
913
914                         // repeatable content simple element
915                         DataColumn cc2 = new DataColumn ();
916                         cc2.ColumnName = elName + "_Column";
917                         cc2.Namespace = el.QualifiedName.Namespace;
918                         cc2.ColumnMapping = MappingType.SimpleContent;
919                         cc2.AllowDBNull = false;
920 #if NET_2_0
921                         cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
922 #else
923                         cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
924 #endif
925
926                         dt.Columns.Add (cc2);
927                         dt.Columns.Add (cc);
928                         dataset.Tables.Add (dt);
929
930                         RelationStructure rel = new RelationStructure ();
931                         rel.ParentTableName = parentName;
932                         rel.ChildTableName = dt.TableName;
933                         rel.ParentColumnName = pkey.ColumnName;
934                         rel.ChildColumnName = cc.ColumnName;
935                         rel.IsNested = true;
936                         rel.CreateConstraint = true;
937                         relations.Add (rel);
938                 }
939
940                 private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
941                 {
942                         col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
943                         col.Namespace = el.QualifiedName.Namespace;
944                         col.ColumnMapping = MappingType.Element;
945 #if NET_2_0
946                         col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
947                         FillFacet (col, el.ElementSchemaType as XmlSchemaSimpleType);
948 #else
949                         col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
950                         FillFacet (col, el.ElementType as XmlSchemaSimpleType);
951 #endif
952
953                         ImportColumnMetaInfo (el, el.QualifiedName, col);
954
955                         AddColumn (col);
956                 }
957
958                 private void AddColumn (DataColumn col)
959                 {
960                         if (col.Ordinal < 0)
961                                 currentTable.NonOrdinalColumns.Add (col);
962                         else
963                                 currentTable.OrdinalColumns.Add (col, col.Ordinal);
964                 }
965
966                 private void FillFacet (DataColumn col, XmlSchemaSimpleType st)
967                 {
968                         if (st == null || st.Content == null)
969                                 return;
970
971                         // Handle restriction facets
972
973                         XmlSchemaSimpleTypeRestriction restriction = st == null ? null : st.Content as XmlSchemaSimpleTypeRestriction;
974                         if (restriction == null)
975                                 throw new DataException ("DataSet does not suport 'list' nor 'union' simple type.");
976
977                         foreach (XmlSchemaFacet f in restriction.Facets) {
978                                 if (f is XmlSchemaMaxLengthFacet)
979                                         // There is no reason why MaxLength is limited to int, except for the fact that DataColumn.MaxLength property is int.
980                                         col.MaxLength = int.Parse (f.Value);
981                         }
982                 }
983
984                 private Type ConvertDatatype (XmlSchemaDatatype dt)
985                 {
986                         if (dt == null)
987                                 return typeof (string);
988                         else if (dt.ValueType == typeof (decimal)) {
989                                 // LAMESPEC: MSDN documentation says it is based 
990                                 // on ValueType. However, in the System.Xml.Schema
991                                 // context, xs:integer is mapped to Decimal, while
992                                 // in DataSet context it is mapped to Int64.
993                                 if (dt == schemaDecimalType)
994                                         return typeof (decimal);
995                                 else if (dt == schemaIntegerType)
996                                         return typeof (long);
997                                 else
998                                         return typeof (ulong);
999                         }
1000                         else
1001                                 return dt.ValueType;
1002                 }
1003
1004                 // This method cuts out the local name of the last step from XPath.
1005                 // It is nothing more than hack. However, MS looks to do similar.
1006                 private string GetSelectorTarget (string xpath)
1007                 {
1008                         string tableName = xpath;
1009                         int index = tableName.LastIndexOf ('/');
1010                         // '>' is enough. If XPath [0] = '/', it is invalid. 
1011                         // Selector can specify only element axes.
1012                         if (index > 0)
1013                                 tableName = tableName.Substring (index + 1);
1014
1015                         // Round QName to NSName
1016                         index = tableName.LastIndexOf (':');
1017                         if (index > 0)
1018                                 tableName = tableName.Substring (index + 1);
1019
1020                         return XmlHelper.Decode (tableName);
1021                 }
1022
1023                 private void ReserveSelfIdentity (XmlSchemaIdentityConstraint ic)
1024                 {
1025                         string tableName = GetSelectorTarget (ic.Selector.XPath);
1026
1027                         string [] cols = new string [ic.Fields.Count];
1028                         bool [] isAttrSpec = new bool [cols.Length];
1029
1030                         int i = 0;
1031                         foreach (XmlSchemaXPath Field in ic.Fields) {
1032                                 string colName = Field.XPath;
1033                                 bool isAttr = colName.Length > 0 && colName [0] == '@';
1034                                 int index = colName.LastIndexOf (':');
1035                                 if (index > 0)
1036                                         colName = colName.Substring (index + 1);
1037                                 else if (isAttr)
1038                                         colName = colName.Substring (1);
1039
1040                                 colName = XmlHelper.Decode (colName);
1041                                 cols [i] = colName;
1042                                 isAttrSpec [i] = isAttr;
1043                                 i++;
1044                         }
1045                         
1046                         bool isPK = false;
1047                         // find if there is an attribute with the constraint name
1048                         // if not use the XmlSchemaConstraint's name.
1049                         string constraintName = ic.Name;
1050                         if (ic.UnhandledAttributes != null) {
1051                                 foreach (XmlAttribute attr in ic.UnhandledAttributes) {
1052                                         if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
1053                                                 continue;
1054                                         switch (attr.LocalName) {
1055                                         case XmlConstants.ConstraintName:
1056                                                 constraintName = attr.Value;
1057                                                 break;
1058                                         case XmlConstants.PrimaryKey:
1059                                                 isPK = bool.Parse(attr.Value);
1060                                                 break;
1061                                         }
1062                                 }
1063                         }
1064                         reservedConstraints.Add (ic,
1065                                 new ConstraintStructure (tableName, cols,
1066                                         isAttrSpec, constraintName, isPK, null, false, false));
1067                 }
1068
1069                 private void ProcessSelfIdentity (ConstraintStructure c)
1070                 {
1071                         // Basic concept came from XmlSchemaMapper.cs
1072
1073                         string tableName = c.TableName;
1074                         
1075                         DataTable dt = dataset.Tables [tableName];
1076                         if (dt == null) {
1077                                 if (forDataSet)
1078                                         throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
1079                                 else
1080                                         // nonexistent table name. .NET ignores it for DataTable.ReadXmlSchema().
1081                                         return;
1082                         }
1083
1084                         DataColumn [] cols = new DataColumn [c.Columns.Length];
1085                         for (int i = 0; i < cols.Length; i++) {
1086                                 string colName = c.Columns [i];
1087                                 bool isAttr = c.IsAttribute [i];
1088                                 DataColumn col = dt.Columns [colName];
1089                                 if (col == null)
1090                                         throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
1091                                 if (isAttr && col.ColumnMapping != MappingType.Attribute)
1092                                         throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
1093                                 if (!isAttr && col.ColumnMapping != MappingType.Element)
1094                                         throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
1095
1096                                 cols [i] = dt.Columns [colName];
1097                         }
1098                         
1099                         bool isPK = c.IsPrimaryKey;
1100                         string constraintName = c.ConstraintName;
1101                         dt.Constraints.Add (new UniqueConstraint (
1102                                 constraintName, cols, isPK));
1103                 }
1104
1105                 private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
1106                 {
1107                         // Basic concept came from XmlSchemaMapper.cs
1108
1109                         string tableName = GetSelectorTarget (keyref.Selector.XPath);
1110
1111                         string [] cols = new string [keyref.Fields.Count];
1112                         bool [] isAttrSpec = new bool [cols.Length];
1113                         int i = 0;
1114                         foreach (XmlSchemaXPath Field in keyref.Fields) {
1115                                 string colName = Field.XPath;
1116                                 bool isAttr = colName.Length > 0 && colName [0] == '@';
1117                                 int index = colName.LastIndexOf (':');
1118                                 if (index > 0)
1119                                         colName = colName.Substring (index + 1);
1120                                 else if (isAttr)
1121                                         colName = colName.Substring (1);
1122
1123                                 colName = XmlHelper.Decode (colName);
1124                                 cols [i] = colName;
1125                                 isAttrSpec [i] = isAttr;
1126                                 i++;
1127                         }
1128                         string constraintName = keyref.Name;
1129                         bool isNested = false;
1130                         bool isConstraintOnly = false;
1131                         if (keyref.UnhandledAttributes != null) {
1132                                 foreach (XmlAttribute attr in keyref.UnhandledAttributes) {
1133                                         if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
1134                                                 continue;
1135                                         switch (attr.LocalName) {
1136                                         case XmlConstants.ConstraintName:
1137                                                 constraintName = attr.Value;
1138                                                 break;
1139                                         case XmlConstants.IsNested:
1140                                                 if (attr.Value == "true")
1141                                                         isNested = true;
1142                                                 break;
1143                                         case XmlConstants.ConstraintOnly:
1144                                                 if (attr.Value == "true")
1145                                                         isConstraintOnly = true;
1146                                                 break;
1147                                         }
1148                                 }
1149                         }
1150
1151                         reservedConstraints.Add (keyref, new ConstraintStructure (
1152                                 tableName, cols, isAttrSpec, constraintName,
1153                                 false, keyref.Refer.Name, isNested, isConstraintOnly));
1154                 }
1155
1156                 private void ProcessRelationIdentity (XmlSchemaElement element, ConstraintStructure c)
1157                 {
1158                         // Basic concept came from XmlSchemaMapper.cs
1159
1160                         string tableName = c.TableName;
1161
1162                         DataColumn [] cols;
1163                         DataTable dt = dataset.Tables [tableName];
1164                         if (dt == null)
1165                                 throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
1166
1167                         cols = new DataColumn [c.Columns.Length];
1168                         for (int i = 0; i < cols.Length; i++) {
1169                                 string colName = c.Columns [i];
1170                                 bool isAttr = c.IsAttribute [i];
1171                                 DataColumn col = dt.Columns [colName];
1172                                 if (isAttr && col.ColumnMapping != MappingType.Attribute)
1173                                         throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
1174                                 if (!isAttr && col.ColumnMapping != MappingType.Element)
1175                                         throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
1176                                 cols [i] = col;
1177                         }
1178                         string name = c.ReferName;
1179                         // get the unique constraint for the releation
1180                         UniqueConstraint uniq = FindConstraint (name, element);
1181                         // generate the FK.
1182                         ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
1183                         dt.Constraints.Add (fkc);
1184
1185                         if (!c.IsConstraintOnly) {
1186                                 // generate the relation.
1187                                 DataRelation rel = new DataRelation (c.ConstraintName, uniq.Columns, cols, true);
1188                                 rel.Nested = c.IsNested;
1189                                 rel.SetParentKeyConstraint (uniq);
1190                                 rel.SetChildKeyConstraint (fkc);
1191
1192                                 dataset.Relations.Add (rel);
1193                         }
1194                 }
1195
1196                 // get the unique constraint for the relation.
1197                 // name - the name of the XmlSchemaUnique element
1198                 private UniqueConstraint FindConstraint (string name, XmlSchemaElement element)
1199                 {
1200                         // Copied from XmlSchemaMapper.cs
1201
1202                         // find the element in the constraint collection.
1203                         foreach (XmlSchemaIdentityConstraint c in element.Constraints) {
1204                                 if (c is XmlSchemaKeyref)
1205                                         continue;
1206
1207                                 if (c.Name == name) {
1208                                         string tableName = GetSelectorTarget (c.Selector.XPath);
1209
1210                                         // find the table in the dataset.
1211                                         DataTable dt = dataset.Tables [tableName];
1212
1213                                         string constraintName = c.Name;
1214                                         // find if there is an attribute with the constraint name
1215                                         // if not use the XmlSchemaUnique name.
1216                                         if (c.UnhandledAttributes != null)
1217                                                 foreach (XmlAttribute attr in c.UnhandledAttributes)
1218                                                         if (attr.LocalName == "ConstraintName" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
1219                                                                 constraintName = attr.Value;
1220                                         return (UniqueConstraint) dt.Constraints [constraintName];
1221                                 }
1222                         }
1223                         throw new DataException ("Target identity constraint was not found: " + name);
1224                 }
1225
1226                 private void HandleAnnotations (XmlSchemaAnnotation an, bool nested)
1227                 {
1228                         foreach (XmlSchemaObject content in an.Items) {
1229                                 XmlSchemaAppInfo ai = content as XmlSchemaAppInfo;
1230                                 if (ai != null) {
1231                                         foreach (XmlNode n in ai.Markup) {
1232                                                 XmlElement el = n as XmlElement;
1233                                                 
1234                                                 // #325464 debugging
1235                                                 //Console.WriteLine ("Name: " + el.LocalName + " NS: " + el.NamespaceURI + " Const: " + XmlConstants.MsdataNamespace);
1236                                                 if (el != null && el.LocalName == "Relationship" && el.NamespaceURI == XmlConstants.MsdataNamespace)
1237                                                         HandleRelationshipAnnotation (el, nested);
1238 #if NET_2_0
1239                                                 if (el != null && el.LocalName == "DataSource" && el.NamespaceURI == XmlConstants.MsdatasourceNamespace)
1240                                                         HandleDataSourceAnnotation (el, nested);
1241 #endif
1242                                         }
1243                                 }
1244                         }
1245                 }
1246
1247 #if NET_2_0
1248                 private void HandleDataSourceAnnotation (XmlElement el, bool nested)
1249                 {
1250                         // Handle: Connections and Tables
1251                         // For Tables: extract the provider information from connection and use
1252                         // the corresponding providerfactory to create the adapter and et al objects 
1253                         // and populate them
1254                         
1255                         // #325464 debugging
1256                         //Console.WriteLine ("In HandleDataSourceAnnotation... ");
1257                         string providerName = null;
1258                         string connString = null;
1259                         DbProviderFactory provider = null;
1260                         XmlElement e, tablesElement = null;
1261                         
1262                         foreach (XmlNode n in el.ChildNodes) {
1263                                 e = n as XmlElement;
1264                                 
1265                                 if (e == null)
1266                                         continue;
1267                                 
1268 #if !MONOTOUCH
1269                                 if (e.LocalName == "Connections") {
1270                                         providerName = ((XmlElement)e.FirstChild).GetAttribute ("Provider");
1271                                         connString = ((XmlElement)e.FirstChild).GetAttribute ("AppSettingsPropertyName");
1272                                         provider = DbProviderFactories.GetFactory (providerName);
1273                                         continue;
1274                                 }
1275 #endif // !MONOTOUCH
1276                                 // #325464 debugging
1277                                 //Console.WriteLine ("ProviderName: " + providerName + "Connstr: " + connString);
1278                                 
1279                                 if (e.LocalName == "Tables")
1280                                         tablesElement = e;
1281                         }
1282                                 
1283                         if (tablesElement != null && provider != null) {
1284                                 foreach (XmlNode node in tablesElement.ChildNodes) {
1285                                         ProcessTableAdapter (node as XmlElement, provider, connString);
1286                                 }
1287                         }
1288                 }
1289                 
1290                 private void ProcessTableAdapter (XmlElement el, DbProviderFactory provider, string connStr)
1291                 {
1292                         XmlElement e;
1293                         string datasetTableName = null;
1294                         
1295                         if (el == null)
1296                                 return;
1297                         
1298                         // #325464 debugging
1299                         //Console.WriteLine ("in ProcessTableAdapters...");
1300                         currentAdapter = new TableAdapterSchemaInfo (provider); 
1301                         currentAdapter.ConnectionString = connStr;
1302                         
1303                         //Console.WriteLine ("Provider: {0}, connection: {1}, adapter: {2}", 
1304                         //                   provider, currentAdapter.Connection, currentAdapter.Adapter);
1305                         currentAdapter.BaseClass = el.GetAttribute ("BaseClass");
1306                         datasetTableName = el.GetAttribute ("Name");
1307                         currentAdapter.Name = el.GetAttribute ("GeneratorDataComponentClassName");
1308                         
1309                         if (String.IsNullOrEmpty (currentAdapter.Name))
1310                                 currentAdapter.Name = el.GetAttribute ("DataAccessorName");
1311
1312                         //Console.WriteLine ("Name: "+currentAdapter.Name);
1313                         foreach (XmlNode n in el.ChildNodes) {
1314                                 e = n as XmlElement;
1315                                 
1316                                 //Console.WriteLine ("Children of Tables: "+e.LocalName);
1317                                 if (e == null)
1318                                         continue;
1319                                 
1320                                 switch (e.LocalName) {
1321                                         case "MainSource": 
1322                                         case "Sources": 
1323                                                 foreach (XmlNode msn in e.ChildNodes)
1324                                                         ProcessDbSource (msn as XmlElement);
1325                                                 break;
1326                                         
1327                                         case "Mappings":
1328                                                 DataTableMapping tableMapping = new DataTableMapping ();
1329                                                 tableMapping.SourceTable = "Table";
1330                                                 tableMapping.DataSetTable = datasetTableName;
1331                                                 
1332                                                 foreach (XmlNode mps in e.ChildNodes)
1333                                                         ProcessColumnMapping (mps as XmlElement, tableMapping);
1334                                                 
1335                                                 currentAdapter.Adapter.TableMappings.Add (tableMapping);
1336                                                 break;                                          
1337                                 }
1338                         }
1339                 }
1340                 
1341                 private void ProcessDbSource (XmlElement el)
1342                 {
1343                         
1344                         string cmdType;
1345                         string tmp = null;
1346                         XmlElement e;
1347                         
1348                         if (el == null)
1349                                 return;
1350                         
1351                         //Console.WriteLine ("ProcessDbSources: "+el.LocalName);
1352
1353                         tmp = el.GetAttribute ("GenerateShortCommands");
1354                         //Console.WriteLine ("GenerateShortCommands: {0}", tmp);
1355                         if (!String.IsNullOrEmpty (tmp))
1356                                 currentAdapter.ShortCommands = Convert.ToBoolean (tmp);
1357                 
1358                         DbCommandInfo cmdInfo = new DbCommandInfo ();
1359                         tmp = el.GetAttribute ("GenerateMethods");
1360                         if (!String.IsNullOrEmpty (tmp)) {
1361                                 DbSourceMethodInfo mthdInfo = null;
1362                                 
1363                                 switch ((GenerateMethodsType) Enum.Parse (typeof (GenerateMethodsType), tmp)) {
1364                                 case GenerateMethodsType.Get:
1365                                         mthdInfo = new DbSourceMethodInfo ();
1366                                         mthdInfo.Name = el.GetAttribute ("GetMethodName");
1367                                         mthdInfo.Modifier = el.GetAttribute ("GetMethodModifier");
1368                                         if (String.IsNullOrEmpty (mthdInfo.Modifier))
1369                                                 mthdInfo.Modifier = "Public";
1370                                         mthdInfo.ScalarCallRetval = el.GetAttribute ("ScalarCallRetval");
1371                                         mthdInfo.QueryType = el.GetAttribute ("QueryType");
1372                                         mthdInfo.MethodType = GenerateMethodsType.Get;
1373                                         cmdInfo.Methods = new DbSourceMethodInfo [1];
1374                                         cmdInfo.Methods[0] = mthdInfo;
1375                                         break;
1376                                         
1377                                 case GenerateMethodsType.Fill:
1378                                         mthdInfo = new DbSourceMethodInfo ();
1379                                         mthdInfo.Name = el.GetAttribute ("FillMethodName");
1380                                         mthdInfo.Modifier = el.GetAttribute ("FillMethodModifier");
1381                                         if (String.IsNullOrEmpty (mthdInfo.Modifier))
1382                                                 mthdInfo.Modifier = "Public";
1383                                         mthdInfo.ScalarCallRetval = null;
1384                                         mthdInfo.QueryType = null;
1385                                         mthdInfo.MethodType = GenerateMethodsType.Fill;
1386                                         cmdInfo.Methods = new DbSourceMethodInfo [1];
1387                                         cmdInfo.Methods[0] = mthdInfo;
1388                                         break;
1389                                         
1390                                 case GenerateMethodsType.Both:
1391                                         mthdInfo = new DbSourceMethodInfo ();
1392                                         // Get
1393                                         mthdInfo.Name = el.GetAttribute ("GetMethodName");
1394                                         mthdInfo.Modifier = el.GetAttribute ("GetMethodModifier");
1395                                         if (String.IsNullOrEmpty (mthdInfo.Modifier))
1396                                                 mthdInfo.Modifier = "Public";
1397                                         mthdInfo.ScalarCallRetval = el.GetAttribute ("ScalarCallRetval");
1398                                         mthdInfo.QueryType = el.GetAttribute ("QueryType");
1399                                         mthdInfo.MethodType = GenerateMethodsType.Get;
1400                                         cmdInfo.Methods = new DbSourceMethodInfo [2];
1401                                         cmdInfo.Methods[0] = mthdInfo;
1402                                         
1403                                         // Fill
1404                                         mthdInfo = new DbSourceMethodInfo ();
1405                                         mthdInfo.Name = el.GetAttribute ("FillMethodName");
1406                                         mthdInfo.Modifier = el.GetAttribute ("FillMethodModifier");
1407                                         if (String.IsNullOrEmpty (mthdInfo.Modifier))
1408                                                 mthdInfo.Modifier = "Public";
1409                                         mthdInfo.ScalarCallRetval = null;
1410                                         mthdInfo.QueryType = null;
1411                                         mthdInfo.MethodType = GenerateMethodsType.Fill;
1412                                         cmdInfo.Methods[1] = mthdInfo;
1413                                         break;
1414                                 }
1415                         } else {
1416                                 // no Get or Fill methods - non <MainSource> sources
1417                                 DbSourceMethodInfo mthdInfo = new DbSourceMethodInfo ();
1418                                 mthdInfo.Name = el.GetAttribute ("Name");
1419                                 mthdInfo.Modifier = el.GetAttribute ("Modifier");
1420                                 if (String.IsNullOrEmpty (mthdInfo.Modifier))
1421                                         mthdInfo.Modifier = "Public";
1422                                 mthdInfo.ScalarCallRetval = el.GetAttribute ("ScalarCallRetval");
1423                                 mthdInfo.QueryType = el.GetAttribute ("QueryType");
1424                                 mthdInfo.MethodType = GenerateMethodsType.None;
1425                                 // Add MethodInfo to DbCommandInfo
1426                                 cmdInfo.Methods = new DbSourceMethodInfo [1];
1427                                 cmdInfo.Methods[0] = mthdInfo;
1428                         }
1429                         
1430                         foreach (XmlNode n in el.ChildNodes) {
1431                                 e = n as XmlElement;
1432                                 
1433                                 if (e == null) 
1434                                         continue;
1435                                 
1436                                 switch (e.LocalName) {
1437                                         case "SelectCommand": 
1438                                                 cmdInfo.Command = ProcessDbCommand (e.FirstChild as XmlElement);
1439                                                 currentAdapter.Commands.Add (cmdInfo);
1440                                                 break;
1441                                         case "InsertCommand": 
1442                                                 currentAdapter.Adapter.InsertCommand = ProcessDbCommand (e.FirstChild as XmlElement);
1443                                                 break;
1444                                         case "UpdateCommand": 
1445                                                 currentAdapter.Adapter.UpdateCommand = ProcessDbCommand (e.FirstChild as XmlElement);
1446                                                 break;
1447                                         case "DeleteCommand": 
1448                                                 currentAdapter.Adapter.DeleteCommand = ProcessDbCommand (e.FirstChild as XmlElement);
1449                                                 break;
1450                                 }
1451                         }
1452                 }
1453                 
1454                 private DbCommand ProcessDbCommand (XmlElement el)
1455                 {
1456                         XmlElement e;
1457                         //Console.WriteLine (el.LocalName);
1458                         string cmdText = null;
1459                         string cmdType = null;
1460                         ArrayList parameters = null;
1461                         
1462                         if (el == null)
1463                                 return null;
1464                         
1465                         cmdType = el.GetAttribute ("CommandType");
1466                         foreach (XmlNode n in el.ChildNodes) {
1467                                 e = n as XmlElement;
1468                                 if (e != null && e.LocalName == "CommandText")
1469                                         cmdText = e.InnerText;
1470                                 else if (e != null && e.LocalName == "Parameters" && !e.IsEmpty)
1471                                         parameters = ProcessDbParameters (e);
1472                         }
1473                         
1474                         DbCommand cmd = currentAdapter.Provider.CreateCommand ();
1475                         cmd.CommandText = cmdText;
1476                         if (cmdType == "StoredProcedure")
1477                                 cmd.CommandType = CommandType.StoredProcedure;
1478                         else
1479                                 cmd.CommandType = CommandType.Text;
1480
1481                         if (parameters != null)
1482                                 cmd.Parameters.AddRange (parameters.ToArray ());
1483                         
1484                         //Console.WriteLine ("Parameters count: {0}", cmd.Parameters.Count);
1485                         return cmd;
1486                 }
1487                 
1488                 private ArrayList ProcessDbParameters (XmlElement el)
1489                 {
1490                         //Console.WriteLine ("ProcessDbParameters: "+el.LocalName);
1491                         string tmp = null;
1492                         XmlElement e;
1493                         DbParameter param = null;
1494                         ArrayList parameters = new ArrayList ();
1495                         
1496                         if (el == null)
1497                                 return parameters;
1498                         
1499                         foreach (XmlNode n in el.ChildNodes) {
1500                                 e = n as XmlElement;
1501                                 
1502                                 if (e == null)
1503                                         continue;
1504                                 param = currentAdapter.Provider.CreateParameter ();
1505
1506                                 tmp = e.GetAttribute ("AllowDbNull");
1507                                 if (!String.IsNullOrEmpty (tmp))
1508                                         param.IsNullable = Convert.ToBoolean (tmp);
1509                                 
1510                                 param.ParameterName = e.GetAttribute ("ParameterName");
1511                                 tmp = e.GetAttribute ("ProviderType");
1512                                 if (!String.IsNullOrEmpty (tmp))
1513                                         tmp = e.GetAttribute ("DbType");
1514                                 param.FrameworkDbType = tmp;
1515                                 
1516                                 tmp = e.GetAttribute ("Direction");
1517                                 param.Direction = (ParameterDirection) Enum.Parse (typeof (ParameterDirection), tmp);
1518                                 
1519                                 ((IDbDataParameter)param).Precision = Convert.ToByte (e.GetAttribute ("Precision"));
1520                                 ((IDbDataParameter)param).Scale = Convert.ToByte (e.GetAttribute ("Scale"));
1521                                 param.Size = Convert.ToInt32 (e.GetAttribute ("Size"));
1522                                 param.SourceColumn = e.GetAttribute ("SourceColumn");
1523                                 
1524                                 tmp = e.GetAttribute ("SourceColumnNullMapping");
1525                                 if (!String.IsNullOrEmpty (tmp))
1526                                         param.SourceColumnNullMapping = Convert.ToBoolean (tmp);
1527                                 
1528                                 tmp = e.GetAttribute ("SourceVersion");
1529                                 param.SourceVersion = (DataRowVersion) Enum.Parse (typeof (DataRowVersion), tmp);                               
1530                                 parameters.Add (param);
1531                         }
1532                         
1533                         return parameters;
1534                 }
1535
1536                 private void ProcessColumnMapping (XmlElement el, DataTableMapping tableMapping)
1537                 {
1538                         if (el == null)
1539                                 return;
1540                         
1541                         tableMapping.ColumnMappings.Add (el.GetAttribute ("SourceColumn"), 
1542                                                          el.GetAttribute ("DataSetColumn"));
1543                 }
1544                 
1545 #endif
1546                 
1547                 private void HandleRelationshipAnnotation (XmlElement el, bool nested)
1548                 {
1549                         string name = el.GetAttribute ("name");
1550                         string ptn = el.GetAttribute ("parent", XmlConstants.MsdataNamespace);
1551                         string ctn = el.GetAttribute ("child", XmlConstants.MsdataNamespace);
1552                         string pkn = el.GetAttribute ("parentkey", XmlConstants.MsdataNamespace);
1553                         string fkn = el.GetAttribute ("childkey", XmlConstants.MsdataNamespace);
1554
1555                         RelationStructure rel = new RelationStructure ();
1556                         rel.ExplicitName = XmlHelper.Decode (name);
1557                         rel.ParentTableName = XmlHelper.Decode (ptn);
1558                         rel.ChildTableName = XmlHelper.Decode (ctn);
1559                         // ColumnNames will be decoded wherever they are used as they can
1560                         // contain 'space' separated list of column-names.
1561                         rel.ParentColumnName = pkn;
1562                         rel.ChildColumnName = fkn;
1563                         rel.IsNested = nested;
1564                         rel.CreateConstraint = false; // by default?
1565                         relations.Add (rel);
1566                 }
1567
1568                 private object GetElementDefaultValue (XmlSchemaElement elem)
1569                 {
1570                         // Unlike attribute, element cannot have a default value.
1571                         if (elem.RefName == XmlQualifiedName.Empty)
1572                                 return elem.DefaultValue;
1573                         XmlSchemaElement referenced = schema.Elements [elem.RefName] as XmlSchemaElement;
1574                         if (referenced == null) // considering missing sub components
1575                                 return null;
1576                         return referenced.DefaultValue;
1577                 }
1578
1579                 private object GetAttributeDefaultValue (XmlSchemaAttribute attr)
1580                 {
1581 #if BUGGY_MS_COMPATIBLE
1582                         if (attr == null)
1583                                 return null;
1584                         else if (attr.RefName != XmlQualifiedName.Empty) {
1585                                 XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
1586                                 if (referenced != null)
1587                                         return referenced.DefaultValue;
1588                                 else
1589                                         return null;
1590                         }
1591                         if (attr.DefaultValue != null)
1592                                 return attr.DefaultValue;
1593                         return attr.FixedValue;
1594 #else
1595                         if (attr.DefaultValue != null)
1596                                 return attr.DefaultValue;
1597                         else if (attr.FixedValue != null)
1598                                 return attr.FixedValue;
1599                         else if (attr.RefName == XmlQualifiedName.Empty)
1600                                 return null;
1601                         XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
1602                         if (referenced == null) // considering missing sub components
1603                                 return null;
1604                         if (referenced.DefaultValue != null)
1605                                 return referenced.DefaultValue;
1606                         return referenced.FixedValue;
1607 #endif
1608                 }
1609         }
1610 }