New tests, updates
[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.Globalization;
163 using System.Xml;
164 using System.Xml.Schema;
165
166
167 namespace System.Data
168 {
169         internal class TableStructureCollection : CollectionBase
170         {
171                 public void Add (TableStructure table)
172                 {
173                         List.Add (table);
174                 }
175
176                 public TableStructure this [int i] {
177                         get { return List [i] as TableStructure; }
178                 }
179
180                 public TableStructure this [string name] {
181                         get {
182                                 foreach (TableStructure ts in List)
183                                         if (ts.Table.TableName == name)
184                                                 return ts;
185                                 return null;
186                         }
187                 }
188         }
189
190         internal class RelationStructureCollection : CollectionBase
191         {
192                 public void Add (RelationStructure rel)
193                 {
194                         List.Add (rel);
195                 }
196
197                 public RelationStructure this [int i] {
198                         get { return List [i] as RelationStructure; }
199                 }
200
201                 public RelationStructure this [string parent, string child] {
202                         get {
203                                 foreach (RelationStructure rel in List)
204                                         if (rel.ParentTableName == parent && rel.ChildTableName == child)
205                                                 return rel;
206                                 return null;
207                         }
208                 }
209         }
210
211         internal class TableStructure
212         {
213                 public TableStructure (DataTable table)
214                 {
215                         this.Table = table;
216                 }
217
218                 // The columns and orders which will be added to the context
219                 // table (See design notes; Because of the ordinal problem)
220                 public DataTable Table;
221                 public Hashtable OrdinalColumns = new Hashtable ();
222                 public ArrayList NonOrdinalColumns = new ArrayList ();
223                 public DataColumn PrimaryKey;
224
225                 public bool ContainsColumn (string name)
226                 {
227                         foreach (DataColumn col in NonOrdinalColumns)
228                                 if (col.ColumnName == name)
229                                         return true;
230                         foreach (DataColumn col in OrdinalColumns.Keys)
231                                 if (col.ColumnName == name)
232                                         return true;
233                         return false;
234                 }
235         }
236
237         internal class RelationStructure
238         {
239                 public string ExplicitName;
240                 public string ParentTableName;
241                 public string ChildTableName;
242                 public string ParentColumnName;
243                 public string ChildColumnName;
244                 public bool IsNested;
245                 public bool CreateConstraint;
246         }
247
248         internal class ConstraintStructure
249         {
250                 public readonly string TableName;
251                 public readonly string [] Columns;
252                 public readonly bool [] IsAttribute;
253                 public readonly string ConstraintName;
254                 public readonly bool IsPrimaryKey;
255                 public readonly string ReferName;
256                 public readonly bool IsNested;
257                 public readonly bool IsConstraintOnly;
258
259                 public ConstraintStructure (string tname, string [] cols, bool [] isAttr, string cname, bool isPK, string refName, bool isNested, bool isConstraintOnly)
260                 {
261                         TableName = tname;
262                         Columns = cols;
263                         IsAttribute = isAttr;
264                         ConstraintName = XmlHelper.Decode (cname);
265                         IsPrimaryKey = isPK;
266                         ReferName = refName;
267                         IsNested = isNested;
268                         IsConstraintOnly = isConstraintOnly;
269                 }
270         }
271
272         internal class XmlSchemaDataImporter
273         {
274                 static readonly XmlSchemaDatatype schemaIntegerType;
275                 static readonly XmlSchemaDatatype schemaDecimalType;
276                 static readonly XmlSchemaComplexType schemaAnyType;
277
278                 static XmlSchemaDataImporter ()
279                 {
280                         XmlSchema s = new XmlSchema ();
281                         XmlSchemaAttribute a = new XmlSchemaAttribute ();
282                         a.Name = "foo";
283                         a.SchemaTypeName = new XmlQualifiedName ("integer", XmlSchema.Namespace);
284                         s.Items.Add (a);
285                         XmlSchemaAttribute b = new XmlSchemaAttribute ();
286                         b.Name = "bar";
287                         b.SchemaTypeName = new XmlQualifiedName ("decimal", XmlSchema.Namespace);
288                         s.Items.Add (b);
289                         XmlSchemaElement e = new XmlSchemaElement ();
290                         e.Name = "bar";
291                         s.Items.Add (e);
292                         s.Compile (null);
293 #if NET_2_0
294                         schemaIntegerType = ((XmlSchemaSimpleType) a.AttributeSchemaType).Datatype;
295                         schemaDecimalType = ((XmlSchemaSimpleType) b.AttributeSchemaType).Datatype;
296                         schemaAnyType = e.ElementSchemaType as XmlSchemaComplexType;
297 #else
298                         schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
299                         schemaDecimalType = b.AttributeType as XmlSchemaDatatype;
300                         schemaAnyType = e.ElementType as XmlSchemaComplexType;
301 #endif
302                 }
303
304                 #region Fields
305
306                 DataSet dataset;
307                 bool forDataSet;
308                 XmlSchema schema;
309
310                 ArrayList relations = new ArrayList ();
311                 Hashtable reservedConstraints = new Hashtable ();
312
313                 // such element that has an attribute msdata:IsDataSet="true"
314                 XmlSchemaElement datasetElement;
315
316                 // choice alternatives in the "dataset element"
317                 ArrayList topLevelElements = new ArrayList ();
318
319                 // import target elements
320                 ArrayList targetElements = new ArrayList ();
321
322                 TableStructure currentTable;
323
324                 #endregion
325
326                 // .ctor()
327
328                 public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
329                 {
330                         this.dataset = dataset;
331                         this.forDataSet = forDataSet;
332                         dataset.DataSetName = "NewDataSet"; // Initialize always
333                         schema = XmlSchema.Read (reader, null);
334                         if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace)
335                                 reader.ReadEndElement ();
336                         schema.Compile (null);
337                 }
338
339                 // methods
340
341                 public void Process ()
342                 {
343                         if (schema.Id != null)
344                                 dataset.DataSetName = schema.Id; // default. Overridable by "DataSet element"
345                         dataset.Namespace = schema.TargetNamespace;
346
347                         // Find dataset element
348                         foreach (XmlSchemaObject obj in schema.Items) {
349                                 XmlSchemaElement el = obj as XmlSchemaElement;
350                                 if (el != null) {
351                                         if (datasetElement == null &&
352                                                 IsDataSetElement (el))
353                                                 datasetElement = el;
354 #if NET_2_0
355                                         if (el.ElementSchemaType is XmlSchemaComplexType &&
356                                             el.ElementSchemaType != schemaAnyType)
357 #else
358                                         if (el.ElementType is XmlSchemaComplexType &&
359                                             el.ElementType != schemaAnyType)
360 #endif
361                                                 targetElements.Add (obj);
362                                 }
363                         }
364
365                         // make reservation of identity constraints
366                         if (datasetElement != null) {
367                                 // keys/uniques.
368                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
369                                         if (! (obj is XmlSchemaKeyref))
370                                                 ReserveSelfIdentity ((XmlSchemaIdentityConstraint) obj);
371                                 // keyrefs.
372                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
373                                         if (obj is XmlSchemaKeyref)
374                                                 ReserveRelationIdentity (datasetElement, (XmlSchemaKeyref) obj);
375                         }
376
377                         foreach (XmlSchemaObject obj in schema.Items) {
378                                 if (obj is XmlSchemaElement) {
379                                         XmlSchemaElement el = obj as XmlSchemaElement;
380 #if NET_2_0
381                                         if (el.ElementSchemaType is XmlSchemaComplexType &&
382                                             el.ElementSchemaType != schemaAnyType)
383 #else
384                                         if (el.ElementType is XmlSchemaComplexType &&
385                                             el.ElementType != schemaAnyType)
386 #endif
387                                                 targetElements.Add (obj);
388                                 }
389                         }
390
391                         // This collection will grow up while processing elements.
392                         int globalElementCount = targetElements.Count;
393
394                         for (int i = 0; i < globalElementCount; i++)
395                                 ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
396
397                         // Rest are local elements.
398                         for (int i = globalElementCount; i < targetElements.Count; i++)
399                                 ProcessDataTableElement ((XmlSchemaElement) targetElements [i]);
400
401                         // Handle relation definitions written as xs:annotation.
402                         // See detail: http://msdn.microsoft.com/library/shared/happyUrl/fnf_msdn.asp?Redirect=%22http://msdn.microsoft.com/404/default.asp%22
403                         foreach (XmlSchemaObject obj in schema.Items)
404                                 if (obj is XmlSchemaAnnotation)
405                                         HandleAnnotations ((XmlSchemaAnnotation) obj, false);
406
407                         if (datasetElement != null) {
408                                 // Handle constraints in the DataSet element. First keys.
409                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
410                                         if (! (obj is XmlSchemaKeyref))
411                                                 ProcessSelfIdentity (reservedConstraints [obj] as ConstraintStructure);
412                                 // Then keyrefs.
413                                 foreach (XmlSchemaObject obj in datasetElement.Constraints)
414                                         if (obj is XmlSchemaKeyref)
415                                                 ProcessRelationIdentity (datasetElement, reservedConstraints [obj] as ConstraintStructure);
416                         }
417
418                         foreach (RelationStructure rs in this.relations)
419                                 dataset.Relations.Add (GenerateRelationship (rs));
420                 }
421
422                 private bool IsDataSetElement (XmlSchemaElement el)
423                 {
424                         if (el.UnhandledAttributes != null) {
425                                 foreach (XmlAttribute attr in el.UnhandledAttributes) {
426                                         if (attr.LocalName == "IsDataSet" &&
427                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace) {
428                                                 switch (attr.Value) {
429                                                 case "true": // case sensitive
430                                                         return true;
431                                                 case "false":
432                                                         break;
433                                                 default:
434                                                         throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
435                                                 }
436                                         }
437                                 }
438                         }
439
440                         if (schema.Elements.Count != 1)
441                                 return false;
442                         if (!(el.SchemaType is XmlSchemaComplexType))
443                                 return false;
444                         XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
445                         if (ct.AttributeUses.Count > 0)
446                                 return false;
447                         XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
448                         if (gb == null || gb.Items.Count == 0)
449                                 return false;
450                         foreach (XmlSchemaParticle p in gb.Items) {
451                                 if (ContainsColumn (p))
452                                         return false;
453                         }
454                         return true;
455                 }
456
457                 private bool ContainsColumn (XmlSchemaParticle p)
458                 {
459                         XmlSchemaElement el = p as XmlSchemaElement;
460                         if (el != null) {
461                                 XmlSchemaComplexType ct = null;
462 #if NET_2_0
463                                 ct = el.ElementSchemaType as XmlSchemaComplexType;
464 #else
465                                 ct = el.ElementType as XmlSchemaComplexType;
466 #endif
467                                 if (ct == null || ct == schemaAnyType)
468                                         return true; // column element
469                                 if (ct.AttributeUses.Count > 0)
470                                         return false; // table element
471                                 if (ct.ContentType == XmlSchemaContentType.TextOnly)
472                                         return true; // column element
473                                 else
474                                         return false; // table element
475                         }
476                         XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
477                         for (int i = 0; i < gb.Items.Count; i++) {
478                                 if (ContainsColumn ((XmlSchemaParticle) gb.Items [i]))
479                                         return true;
480                         }
481                         return false;
482                 }
483
484                 private void ProcessGlobalElement (XmlSchemaElement el)
485                 {
486                         // If it is already registered (by resolving reference
487                         // in previously-imported elements), just ignore.
488                         if (dataset.Tables.Contains (el.QualifiedName.Name))
489                                 return;
490
491                         // If type is not complex, just skip this element
492 #if NET_2_0
493                         if (! (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType))
494 #else
495                         if (! (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType))
496 #endif
497                                 return;
498
499                         if (IsDataSetElement (el)) {
500                                 ProcessDataSetElement (el);
501                                 return;
502                         }
503                         else
504                                 dataset.Locale = CultureInfo.CurrentCulture;
505
506                         // Register as a top-level element
507                         topLevelElements.Add (el);
508                         // Create DataTable for this element
509                         ProcessDataTableElement (el);
510                 }
511
512                 private void ProcessDataSetElement (XmlSchemaElement el)
513                 {
514                         dataset.DataSetName = el.Name;
515                         this.datasetElement = el;
516
517                         // Search for locale attributes
518                         bool useCurrent = false;
519                         if (el.UnhandledAttributes != null) {
520                                 foreach (XmlAttribute attr in el.UnhandledAttributes) {
521 #if NET_2_0
522                                         if (attr.LocalName == "UseCurrentLocale" &&
523                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace)
524                                                 useCurrent = true;
525 #endif
526                                         if (attr.LocalName == "Locale" &&
527                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace) {
528                                                 CultureInfo ci = new CultureInfo (attr.Value);
529                                                 dataset.Locale = ci;
530                                         }
531                                 }
532                         }
533 #if NET_2_0
534                         if (!useCurrent && !dataset.LocaleSpecified) // then set current culture instance _explicitly_
535                                 dataset.Locale = CultureInfo.CurrentCulture;
536 #endif
537
538                         // Process content type particle (and create DataTable)
539                         XmlSchemaComplexType ct = null;
540 #if NET_2_0
541                         ct = el.ElementSchemaType as XmlSchemaComplexType;
542 #else
543                         ct = el.ElementType as XmlSchemaComplexType;
544 #endif
545                         XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
546                         if (p != null)
547                                 HandleDataSetContentTypeParticle (p);
548                 }
549
550                 private void HandleDataSetContentTypeParticle (XmlSchemaParticle p)
551                 {
552                         XmlSchemaElement el = p as XmlSchemaElement;
553                         if (el != null) {
554 #if NET_2_0
555                                 if (el.ElementSchemaType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
556 #else
557                                 if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
558 #endif
559                                         ProcessDataTableElement (el);
560                         }
561                         else if (p is XmlSchemaGroupBase) {
562                                 foreach (XmlSchemaParticle pc in ((XmlSchemaGroupBase) p).Items)
563                                         HandleDataSetContentTypeParticle (pc);
564                         }
565                 }
566
567                 private void ProcessDataTableElement (XmlSchemaElement el)
568                 {
569                         string tableName = XmlHelper.Decode (el.QualifiedName.Name);
570                         // If it is already registered, just ignore.
571                         if (dataset.Tables.Contains (tableName))
572                                 return;
573
574                         DataTable table = new DataTable (tableName);
575                         table.Namespace = el.QualifiedName.Namespace;
576                         TableStructure oldTable = currentTable;
577                         currentTable = new TableStructure (table);
578
579                         dataset.Tables.Add (table);
580
581                         // Find Locale
582                         if (el.UnhandledAttributes != null) {
583                                 foreach (XmlAttribute attr in el.UnhandledAttributes) {
584                                         if (attr.LocalName == "Locale" &&
585                                                 attr.NamespaceURI == XmlConstants.MsdataNamespace)
586                                                 table.Locale = new CultureInfo (attr.Value);
587                                 }
588                         }
589
590                         // Handle complex type (NOTE: It is (or should be)
591                         // impossible the type is other than complex type).
592                         XmlSchemaComplexType ct = null;
593 #if NET_2_0
594                         ct = (XmlSchemaComplexType) el.ElementSchemaType;
595 #else
596                         ct = (XmlSchemaComplexType) el.ElementType;
597 #endif
598
599                         // Handle attributes
600                         foreach (DictionaryEntry de in ct.AttributeUses)
601                                 ImportColumnAttribute ((XmlSchemaAttribute) de.Value);
602
603                         // Handle content type particle
604                         if (ct.ContentTypeParticle is XmlSchemaElement)
605                                 ImportColumnElement (el, (XmlSchemaElement) ct.ContentTypeParticle);
606                         else if (ct.ContentTypeParticle is XmlSchemaGroupBase)
607                                 ImportColumnGroupBase (el, (XmlSchemaGroupBase) ct.ContentTypeParticle);
608                         // else if null then do nothing.
609
610                         // Handle simple content
611                         switch (ct.ContentType) {
612                         case XmlSchemaContentType.TextOnly:
613 //                      case XmlSchemaContentType.Mixed:
614                                 // LAMESPEC: When reading from XML Schema, it maps to "_text", while on the data inference, it is mapped to "_Text" (case ignorant).
615                                 string simpleName = el.QualifiedName.Name + "_text";
616                                 DataColumn simple = new DataColumn (simpleName);
617                                 simple.Namespace = el.QualifiedName.Namespace;
618                                 simple.AllowDBNull = (el.MinOccurs == 0);
619                                 simple.ColumnMapping = MappingType.SimpleContent;
620                                 simple.DataType = ConvertDatatype (ct.Datatype);
621                                 currentTable.NonOrdinalColumns.Add (simple);
622                                 break;
623                         }
624
625                         // add columns to the table in specified order 
626                         // (by msdata:Ordinal attributes)
627                         SortedList sd = new SortedList ();
628                         foreach (DictionaryEntry de in currentTable.OrdinalColumns)
629                                 sd.Add (de.Value, de.Key);
630                         foreach (DictionaryEntry de in sd)
631                                 table.Columns.Add ((DataColumn) de.Value);
632                         foreach (DataColumn dc in currentTable.NonOrdinalColumns)
633                                 table.Columns.Add (dc);
634
635                         currentTable = oldTable;
636                 }
637
638                 private DataRelation GenerateRelationship (RelationStructure rs)
639                 {
640                         DataTable ptab = dataset.Tables [rs.ParentTableName];
641                         DataTable ctab = dataset.Tables [rs.ChildTableName];
642
643                         DataRelation rel ;
644                         string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
645
646                         // Annotation Relations belonging to a DataSet can contain multiple colnames
647                         // in parentkey and childkey.
648                         if (datasetElement != null) {
649                                 String[] pcolnames = rs.ParentColumnName.Split (null);
650                                 String[] ccolnames = rs.ChildColumnName.Split (null);
651
652                                 DataColumn[] pcol = new DataColumn [pcolnames.Length];
653                                 for (int i=0; i<pcol.Length; ++i)
654                                         pcol [i] = ptab.Columns [XmlHelper.Decode (pcolnames [i])];
655
656                                 DataColumn[] ccol = new DataColumn [ccolnames.Length];
657                                 for (int i=0; i < ccol.Length; ++i) {
658                                         ccol [i] = ctab.Columns [XmlHelper.Decode (ccolnames [i])];
659                                         if (ccol [i] == null)
660                                                 ccol [i] = CreateChildColumn (pcol [i], ctab);
661                                 }
662                                 rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
663                         } else {
664                                 DataColumn pcol = ptab.Columns [XmlHelper.Decode (rs.ParentColumnName)];
665                                 DataColumn ccol = ctab.Columns [XmlHelper.Decode (rs.ChildColumnName)];
666                                 if (ccol == null) 
667                                         ccol = CreateChildColumn (pcol, ctab);
668                                 rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
669                         }
670                         rel.Nested = rs.IsNested;
671                         if (rs.CreateConstraint)
672                                 rel.ParentTable.PrimaryKey = rel.ParentColumns;
673                         return rel;
674                 }
675
676                 private DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
677                 {
678                         DataColumn col = childTable.Columns.Add (parentColumn.ColumnName, 
679                                                                 parentColumn.DataType);
680                         col.Namespace = String.Empty;
681                         col.ColumnMapping = MappingType.Hidden;
682                         return col;
683                 }
684
685                 private void ImportColumnGroupBase (XmlSchemaElement parent, XmlSchemaGroupBase gb)
686                 {
687                         foreach (XmlSchemaParticle p in gb.Items) {
688                                 XmlSchemaElement el = p as XmlSchemaElement;
689                                 if (el != null)
690                                         ImportColumnElement (parent, el);
691                                 else if (p is XmlSchemaGroupBase)
692                                         ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
693                                 // otherwise p is xs:any
694                         }
695                 }
696
697                 private XmlSchemaDatatype GetSchemaPrimitiveType (object type)
698                 {
699                         if (type is XmlSchemaComplexType)
700                                 return null; // It came here, so that maybe it is xs:anyType
701                         XmlSchemaDatatype dt = type as XmlSchemaDatatype;
702                         if (dt == null && type != null)
703                                 dt = ((XmlSchemaSimpleType) type).Datatype;
704                         return dt;
705                 }
706
707                 // Note that this column might be Hidden
708                 private void ImportColumnAttribute (XmlSchemaAttribute attr)
709                 {
710                         DataColumn col = new DataColumn ();
711                         col.ColumnName = attr.QualifiedName.Name;
712                         col.Namespace = attr.QualifiedName.Namespace;
713                         XmlSchemaDatatype dt = null;
714 #if NET_2_0
715                         dt = GetSchemaPrimitiveType (((XmlSchemaSimpleType) attr.AttributeSchemaType).Datatype);
716 #else
717                         dt = GetSchemaPrimitiveType (attr.AttributeType);
718 #endif
719                         // This complicated check comes from the fact that
720                         // MS.NET fails to map System.Object to anyType (that
721                         // will cause ReadTypedObject() fail on XmlValidatingReader).
722                         // ONLY In DataSet context, we set System.String for
723                         // simple ur-type.
724                         col.DataType = ConvertDatatype (dt);
725                         if (col.DataType == typeof (object))
726                                 col.DataType = typeof (string);
727                         // When attribute use="prohibited", then it is regarded as 
728                         // Hidden column.
729                         if (attr.Use == XmlSchemaUse.Prohibited)
730                                 col.ColumnMapping = MappingType.Hidden;
731                         else {
732                                 col.ColumnMapping = MappingType.Attribute;
733                                 col.DefaultValue = GetAttributeDefaultValue (attr);
734                         }
735                         if (attr.Use == XmlSchemaUse.Required)
736                                 col.AllowDBNull = false;
737
738 #if NET_2_0
739                         FillFacet (col, attr.AttributeSchemaType as XmlSchemaSimpleType);
740 #else
741                         FillFacet (col, attr.AttributeType as XmlSchemaSimpleType);
742 #endif
743
744                         // Call this method after filling the name
745                         ImportColumnMetaInfo (attr, attr.QualifiedName, col);
746                         AddColumn (col);
747                 }
748
749                 private void ImportColumnElement (XmlSchemaElement parent, XmlSchemaElement el)
750                 {
751                         // FIXME: element nest check
752
753                         DataColumn col = new DataColumn ();
754                         col.DefaultValue = GetElementDefaultValue (el);
755                         col.AllowDBNull = (el.MinOccurs == 0);
756
757 #if NET_2_0
758                         if (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType)
759 #else
760                         if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType)
761 #endif
762                                 FillDataColumnComplexElement (parent, el, col);
763                         else if (el.MaxOccurs != 1)
764                                 FillDataColumnRepeatedSimpleElement (parent, el, col);
765                         else
766                                 FillDataColumnSimpleElement (el, col);
767                 }
768
769                 // common process for element and attribute
770                 private void ImportColumnMetaInfo (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
771                 {
772                         if (obj.UnhandledAttributes != null) {
773                                 foreach (XmlAttribute attr in obj.UnhandledAttributes) {
774                                         if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
775                                                 continue;
776                                         switch (attr.LocalName) {
777                                         case XmlConstants.Caption:
778                                                 col.Caption = attr.Value;
779                                                 break;
780                                         case XmlConstants.DataType:
781                                                 col.DataType = Type.GetType (attr.Value);
782                                                 break;
783                                         case XmlConstants.AutoIncrement:
784                                                 col.AutoIncrement = bool.Parse (attr.Value);
785                                                 break;
786                                         case XmlConstants.AutoIncrementSeed:
787                                                 col.AutoIncrementSeed = int.Parse (attr.Value);
788                                                 break;
789                                         case XmlConstants.AutoIncrementStep:
790                                                 col.AutoIncrementStep = int.Parse (attr.Value);
791                                                 break;
792                                         case XmlConstants.ReadOnly:
793                                                 col.ReadOnly = XmlConvert.ToBoolean (attr.Value);
794                                                 break;
795                                         case XmlConstants.Ordinal:
796                                                 int ordinal = int.Parse (attr.Value);
797                                                 break;
798                                         }
799                                 }
800                         }
801                 }
802
803                 private void FillDataColumnComplexElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
804                 {
805                         if (targetElements.Contains (el))
806                                 return; // do nothing
807
808                         string elName = XmlHelper.Decode (el.QualifiedName.Name);
809                         if (elName == dataset.DataSetName)
810                                 // Well, why it is ArgumentException :-?
811                                 throw new ArgumentException ("Nested element must not have the same name as DataSet's name.");
812
813                         if (el.Annotation != null)
814                                 HandleAnnotations (el.Annotation, true);
815                         // If xsd:keyref xsd:key for this table exists, then don't add
816                         // relation here manually.
817                         else if (!DataSetDefinesKey (elName)) {
818                                 AddParentKeyColumn (parent, el, col);
819
820                                 RelationStructure rel = new RelationStructure ();
821                                 rel.ParentTableName = XmlHelper.Decode (parent.QualifiedName.Name);
822                                 rel.ChildTableName = elName;
823                                 rel.ParentColumnName = col.ColumnName;
824                                 rel.ChildColumnName = col.ColumnName;
825                                 rel.CreateConstraint = true;
826                                 rel.IsNested = true;
827                                 relations.Add (rel);
828                         }
829
830                         // If the element is not referenced one, the element will be handled later.
831                         if (el.RefName == XmlQualifiedName.Empty)
832                                 ProcessDataTableElement (el);
833
834                 }
835
836                 private bool DataSetDefinesKey (string name)
837                 {
838                         foreach (ConstraintStructure c in reservedConstraints.Values)
839                                 if (c.TableName == name && (c.IsPrimaryKey || c.IsNested))
840                                         return true;
841                         return false;
842                 }
843
844                 private void AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
845                 {
846                         if (currentTable.PrimaryKey != null)
847                                 return;
848
849                         // check name identity
850                         string name = XmlHelper.Decode (parent.QualifiedName.Name) + "_Id";
851                         int count = 0;
852                         while (currentTable.ContainsColumn (name))
853                                 name = String.Format ("{0}_{1}", name, count++);
854                         // check existing primary key
855                         if (currentTable.Table.PrimaryKey.Length > 0)
856                                 throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", currentTable.Table.TableName));
857
858                         col.ColumnName = name;
859                         col.ColumnMapping = MappingType.Hidden;
860                         col.Namespace = parent.QualifiedName.Namespace;
861                         col.DataType = typeof (int);
862                         col.AutoIncrement = true;
863                         col.AllowDBNull = false;
864
865                         ImportColumnMetaInfo (el, el.QualifiedName, col);
866                         AddColumn (col);
867                         currentTable.PrimaryKey = col;
868                 }
869
870                 private void FillDataColumnRepeatedSimpleElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
871                 {
872                         if (targetElements.Contains (el))
873                                 return; // do nothing
874
875                         AddParentKeyColumn (parent, el, col);
876                         DataColumn pkey = currentTable.PrimaryKey;
877
878                         string elName = XmlHelper.Decode (el.QualifiedName.Name);
879                         string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
880
881                         DataTable dt = new DataTable ();
882                         dt.TableName = elName;
883                         dt.Namespace = el.QualifiedName.Namespace;
884                         // reference key column to parent
885                         DataColumn cc = new DataColumn ();
886                         cc.ColumnName = parentName + "_Id";
887                         cc.Namespace = parent.QualifiedName.Namespace;
888                         cc.ColumnMapping = MappingType.Hidden;
889                         cc.DataType = typeof (int);
890
891                         // repeatable content simple element
892                         DataColumn cc2 = new DataColumn ();
893                         cc2.ColumnName = elName + "_Column";
894                         cc2.Namespace = el.QualifiedName.Namespace;
895                         cc2.ColumnMapping = MappingType.SimpleContent;
896                         cc2.AllowDBNull = false;
897 #if NET_2_0
898                         cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
899 #else
900                         cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
901 #endif
902
903                         dt.Columns.Add (cc2);
904                         dt.Columns.Add (cc);
905                         dataset.Tables.Add (dt);
906
907                         RelationStructure rel = new RelationStructure ();
908                         rel.ParentTableName = parentName;
909                         rel.ChildTableName = dt.TableName;
910                         rel.ParentColumnName = pkey.ColumnName;
911                         rel.ChildColumnName = cc.ColumnName;
912                         rel.IsNested = true;
913                         rel.CreateConstraint = true;
914                         relations.Add (rel);
915                 }
916
917                 private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
918                 {
919                         col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
920                         col.Namespace = el.QualifiedName.Namespace;
921                         col.ColumnMapping = MappingType.Element;
922 #if NET_2_0
923                         col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
924                         FillFacet (col, el.ElementSchemaType as XmlSchemaSimpleType);
925 #else
926                         col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
927                         FillFacet (col, el.ElementType as XmlSchemaSimpleType);
928 #endif
929
930                         ImportColumnMetaInfo (el, el.QualifiedName, col);
931
932                         AddColumn (col);
933                 }
934
935                 private void AddColumn (DataColumn col)
936                 {
937                         if (col.Ordinal < 0)
938                                 currentTable.NonOrdinalColumns.Add (col);
939                         else
940                                 currentTable.OrdinalColumns.Add (col, col.Ordinal);
941                 }
942
943                 private void FillFacet (DataColumn col, XmlSchemaSimpleType st)
944                 {
945                         if (st == null || st.Content == null)
946                                 return;
947
948                         // Handle restriction facets
949
950                         XmlSchemaSimpleTypeRestriction restriction = st == null ? null : st.Content as XmlSchemaSimpleTypeRestriction;
951                         if (restriction == null)
952                                 throw new DataException ("DataSet does not suport 'list' nor 'union' simple type.");
953
954                         foreach (XmlSchemaFacet f in restriction.Facets) {
955                                 if (f is XmlSchemaMaxLengthFacet)
956                                         // There is no reason why MaxLength is limited to int, except for the fact that DataColumn.MaxLength property is int.
957                                         col.MaxLength = int.Parse (f.Value);
958                         }
959                 }
960
961                 private Type ConvertDatatype (XmlSchemaDatatype dt)
962                 {
963                         if (dt == null)
964                                 return typeof (string);
965                         else if (dt.ValueType == typeof (decimal)) {
966                                 // LAMESPEC: MSDN documentation says it is based 
967                                 // on ValueType. However, in the System.Xml.Schema
968                                 // context, xs:integer is mapped to Decimal, while
969                                 // in DataSet context it is mapped to Int64.
970                                 if (dt == schemaDecimalType)
971                                         return typeof (decimal);
972                                 else if (dt == schemaIntegerType)
973                                         return typeof (long);
974                                 else
975                                         return typeof (ulong);
976                         }
977                         else
978                                 return dt.ValueType;
979                 }
980
981                 // This method cuts out the local name of the last step from XPath.
982                 // It is nothing more than hack. However, MS looks to do similar.
983                 private string GetSelectorTarget (string xpath)
984                 {
985                         string tableName = xpath;
986                         int index = tableName.LastIndexOf ('/');
987                         // '>' is enough. If XPath [0] = '/', it is invalid. 
988                         // Selector can specify only element axes.
989                         if (index > 0)
990                                 tableName = tableName.Substring (index + 1);
991
992                         // Round QName to NSName
993                         index = tableName.LastIndexOf (':');
994                         if (index > 0)
995                                 tableName = tableName.Substring (index + 1);
996
997                         return XmlHelper.Decode (tableName);
998                 }
999
1000                 private void ReserveSelfIdentity (XmlSchemaIdentityConstraint ic)
1001                 {
1002                         string tableName = GetSelectorTarget (ic.Selector.XPath);
1003
1004                         string [] cols = new string [ic.Fields.Count];
1005                         bool [] isAttrSpec = new bool [cols.Length];
1006
1007                         int i = 0;
1008                         foreach (XmlSchemaXPath Field in ic.Fields) {
1009                                 string colName = Field.XPath;
1010                                 bool isAttr = colName.Length > 0 && colName [0] == '@';
1011                                 int index = colName.LastIndexOf (':');
1012                                 if (index > 0)
1013                                         colName = colName.Substring (index + 1);
1014                                 else if (isAttr)
1015                                         colName = colName.Substring (1);
1016
1017                                 colName = XmlHelper.Decode (colName);
1018                                 cols [i] = colName;
1019                                 isAttrSpec [i] = isAttr;
1020                                 i++;
1021                         }
1022                         
1023                         bool isPK = false;
1024                         // find if there is an attribute with the constraint name
1025                         // if not use the XmlSchemaConstraint's name.
1026                         string constraintName = ic.Name;
1027                         if (ic.UnhandledAttributes != null) {
1028                                 foreach (XmlAttribute attr in ic.UnhandledAttributes) {
1029                                         if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
1030                                                 continue;
1031                                         switch (attr.LocalName) {
1032                                         case XmlConstants.ConstraintName:
1033                                                 constraintName = attr.Value;
1034                                                 break;
1035                                         case XmlConstants.PrimaryKey:
1036                                                 isPK = bool.Parse(attr.Value);
1037                                                 break;
1038                                         }
1039                                 }
1040                         }
1041                         reservedConstraints.Add (ic,
1042                                 new ConstraintStructure (tableName, cols,
1043                                         isAttrSpec, constraintName, isPK, null, false, false));
1044                 }
1045
1046                 private void ProcessSelfIdentity (ConstraintStructure c)
1047                 {
1048                         // Basic concept came from XmlSchemaMapper.cs
1049
1050                         string tableName = c.TableName;
1051                         
1052                         DataTable dt = dataset.Tables [tableName];
1053                         if (dt == null) {
1054                                 if (forDataSet)
1055                                         throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
1056                                 else
1057                                         // nonexistent table name. .NET ignores it for DataTable.ReadXmlSchema().
1058                                         return;
1059                         }
1060
1061                         DataColumn [] cols = new DataColumn [c.Columns.Length];
1062                         for (int i = 0; i < cols.Length; i++) {
1063                                 string colName = c.Columns [i];
1064                                 bool isAttr = c.IsAttribute [i];
1065                                 DataColumn col = dt.Columns [colName];
1066                                 if (col == null)
1067                                         throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
1068                                 if (isAttr && col.ColumnMapping != MappingType.Attribute)
1069                                         throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
1070                                 if (!isAttr && col.ColumnMapping != MappingType.Element)
1071                                         throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
1072
1073                                 cols [i] = dt.Columns [colName];
1074                         }
1075                         
1076                         bool isPK = c.IsPrimaryKey;
1077                         string constraintName = c.ConstraintName;
1078                         dt.Constraints.Add (new UniqueConstraint (
1079                                 constraintName, cols, isPK));
1080                 }
1081
1082                 private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
1083                 {
1084                         // Basic concept came from XmlSchemaMapper.cs
1085
1086                         string tableName = GetSelectorTarget (keyref.Selector.XPath);
1087
1088                         string [] cols = new string [keyref.Fields.Count];
1089                         bool [] isAttrSpec = new bool [cols.Length];
1090                         int i = 0;
1091                         foreach (XmlSchemaXPath Field in keyref.Fields) {
1092                                 string colName = Field.XPath;
1093                                 bool isAttr = colName.Length > 0 && colName [0] == '@';
1094                                 int index = colName.LastIndexOf (':');
1095                                 if (index > 0)
1096                                         colName = colName.Substring (index + 1);
1097                                 else if (isAttr)
1098                                         colName = colName.Substring (1);
1099
1100                                 colName = XmlHelper.Decode (colName);
1101                                 cols [i] = colName;
1102                                 isAttrSpec [i] = isAttr;
1103                                 i++;
1104                         }
1105                         string constraintName = keyref.Name;
1106                         bool isNested = false;
1107                         bool isConstraintOnly = false;
1108                         if (keyref.UnhandledAttributes != null) {
1109                                 foreach (XmlAttribute attr in keyref.UnhandledAttributes) {
1110                                         if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
1111                                                 continue;
1112                                         switch (attr.LocalName) {
1113                                         case XmlConstants.ConstraintName:
1114                                                 constraintName = attr.Value;
1115                                                 break;
1116                                         case XmlConstants.IsNested:
1117                                                 if (attr.Value == "true")
1118                                                         isNested = true;
1119                                                 break;
1120                                         case XmlConstants.ConstraintOnly:
1121                                                 if (attr.Value == "true")
1122                                                         isConstraintOnly = true;
1123                                                 break;
1124                                         }
1125                                 }
1126                         }
1127
1128                         reservedConstraints.Add (keyref, new ConstraintStructure (
1129                                 tableName, cols, isAttrSpec, constraintName,
1130                                 false, keyref.Refer.Name, isNested, isConstraintOnly));
1131                 }
1132
1133                 private void ProcessRelationIdentity (XmlSchemaElement element, ConstraintStructure c)
1134                 {
1135                         // Basic concept came from XmlSchemaMapper.cs
1136
1137                         string tableName = c.TableName;
1138
1139                         DataColumn [] cols;
1140                         DataTable dt = dataset.Tables [tableName];
1141                         if (dt == null)
1142                                 throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
1143
1144                         cols = new DataColumn [c.Columns.Length];
1145                         for (int i = 0; i < cols.Length; i++) {
1146                                 string colName = c.Columns [i];
1147                                 bool isAttr = c.IsAttribute [i];
1148                                 DataColumn col = dt.Columns [colName];
1149                                 if (isAttr && col.ColumnMapping != MappingType.Attribute)
1150                                         throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
1151                                 if (!isAttr && col.ColumnMapping != MappingType.Element)
1152                                         throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
1153                                 cols [i] = col;
1154                         }
1155                         string name = c.ReferName;
1156                         // get the unique constraint for the releation
1157                         UniqueConstraint uniq = FindConstraint (name, element);
1158                         // generate the FK.
1159                         ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
1160                         dt.Constraints.Add (fkc);
1161
1162                         if (!c.IsConstraintOnly) {
1163                                 // generate the relation.
1164                                 DataRelation rel = new DataRelation (c.ConstraintName, uniq.Columns, cols, true);
1165                                 rel.Nested = c.IsNested;
1166                                 rel.SetParentKeyConstraint (uniq);
1167                                 rel.SetChildKeyConstraint (fkc);
1168
1169                                 dataset.Relations.Add (rel);
1170                         }
1171                 }
1172
1173                 // get the unique constraint for the relation.
1174                 // name - the name of the XmlSchemaUnique element
1175                 private UniqueConstraint FindConstraint (string name, XmlSchemaElement element)
1176                 {
1177                         // Copied from XmlSchemaMapper.cs
1178
1179                         // find the element in the constraint collection.
1180                         foreach (XmlSchemaIdentityConstraint c in element.Constraints) {
1181                                 if (c is XmlSchemaKeyref)
1182                                         continue;
1183
1184                                 if (c.Name == name) {
1185                                         string tableName = GetSelectorTarget (c.Selector.XPath);
1186
1187                                         // find the table in the dataset.
1188                                         DataTable dt = dataset.Tables [tableName];
1189
1190                                         string constraintName = c.Name;
1191                                         // find if there is an attribute with the constraint name
1192                                         // if not use the XmlSchemaUnique name.
1193                                         if (c.UnhandledAttributes != null)
1194                                                 foreach (XmlAttribute attr in c.UnhandledAttributes)
1195                                                         if (attr.LocalName == "ConstraintName" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
1196                                                                 constraintName = attr.Value;
1197                                         return (UniqueConstraint) dt.Constraints [constraintName];
1198                                 }
1199                         }
1200                         throw new DataException ("Target identity constraint was not found: " + name);
1201                 }
1202
1203                 private void HandleAnnotations (XmlSchemaAnnotation an, bool nested)
1204                 {
1205                         foreach (XmlSchemaObject content in an.Items) {
1206                                 XmlSchemaAppInfo ai = content as XmlSchemaAppInfo;
1207                                 if (ai != null) {
1208                                         foreach (XmlNode n in ai.Markup) {
1209                                                 XmlElement el = n as XmlElement;
1210                                                 if (el != null && el.LocalName == "Relationship" && el.NamespaceURI == XmlConstants.MsdataNamespace)
1211                                                         HandleRelationshipAnnotation (el, nested);
1212                                         }
1213                                 }
1214                         }
1215                 }
1216
1217                 private void HandleRelationshipAnnotation (XmlElement el, bool nested)
1218                 {
1219                         string name = el.GetAttribute ("name");
1220                         string ptn = el.GetAttribute ("parent", XmlConstants.MsdataNamespace);
1221                         string ctn = el.GetAttribute ("child", XmlConstants.MsdataNamespace);
1222                         string pkn = el.GetAttribute ("parentkey", XmlConstants.MsdataNamespace);
1223                         string fkn = el.GetAttribute ("childkey", XmlConstants.MsdataNamespace);
1224
1225                         RelationStructure rel = new RelationStructure ();
1226                         rel.ExplicitName = XmlHelper.Decode (name);
1227                         rel.ParentTableName = XmlHelper.Decode (ptn);
1228                         rel.ChildTableName = XmlHelper.Decode (ctn);
1229                         // ColumnNames will be decoded wherever they are used as they can
1230                         // contain 'space' separated list of column-names.
1231                         rel.ParentColumnName = pkn;
1232                         rel.ChildColumnName = fkn;
1233                         rel.IsNested = nested;
1234                         rel.CreateConstraint = false; // by default?
1235                         relations.Add (rel);
1236                 }
1237
1238                 private object GetElementDefaultValue (XmlSchemaElement elem)
1239                 {
1240                         // Unlike attribute, element cannot have a default value.
1241                         if (elem.RefName == XmlQualifiedName.Empty)
1242                                 return elem.DefaultValue;
1243                         XmlSchemaElement referenced = schema.Elements [elem.RefName] as XmlSchemaElement;
1244                         if (referenced == null) // considering missing sub components
1245                                 return null;
1246                         return referenced.DefaultValue;
1247                 }
1248
1249                 private object GetAttributeDefaultValue (XmlSchemaAttribute attr)
1250                 {
1251 #if BUGGY_MS_COMPATIBLE
1252                         if (attr == null)
1253                                 return null;
1254                         else if (attr.RefName != XmlQualifiedName.Empty) {
1255                                 XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
1256                                 if (referenced != null)
1257                                         return referenced.DefaultValue;
1258                                 else
1259                                         return null;
1260                         }
1261                         if (attr.DefaultValue != null)
1262                                 return attr.DefaultValue;
1263                         return attr.FixedValue;
1264 #else
1265                         if (attr.DefaultValue != null)
1266                                 return attr.DefaultValue;
1267                         else if (attr.FixedValue != null)
1268                                 return attr.FixedValue;
1269                         else if (attr.RefName == XmlQualifiedName.Empty)
1270                                 return null;
1271                         XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
1272                         if (referenced == null) // considering missing sub components
1273                                 return null;
1274                         if (referenced.DefaultValue != null)
1275                                 return referenced.DefaultValue;
1276                         return referenced.FixedValue;
1277 #endif
1278                 }
1279         }
1280 }