2 // XmlSchemaDataImporter.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
10 // ***** The design note became somewhat obsolete. Should be rewritten. *****
16 // This class is used to import an XML Schema into a DataSet schema.
18 // Only XmlReader is acceptable as the input to the class.
19 // This class is not expected to read XML Schema multi time.
21 // ** Targetable Schema Components
23 // Only global global elements that hold complex type are converted
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).
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
35 // Unused complex types are never be converted.
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.
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.
47 // Ignore this section. Microsoft.NET was buggy enough to confuse
48 // against these name conflicts.
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.
56 // ** DataSet element definition
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.
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.
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.
73 // All global elements are considered as an alternative in the dataset
76 // For local elements, msdata:IsDataSet are just ignored.
78 // ** Importing Complex Types as Columns
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
84 // DataColumn has a property MappingType that shows whether this column
85 // came from attribute or element.
87 // [Question: How about MappingType.Simple? How is it used?]
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).
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".
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
103 // "Nested elements" are not allowed. (Clarification required?)
105 // ** Identity Constraints and DataRelations
107 // *** DataRelations from element identity constraints
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).
113 // xs:key and xs:unique are handled as the same (then both will be
114 // serialized as xs:unique).
116 // The XPath expressions in the constraints are strictly limited; they
117 // are expected to be expandable enough to be mappable for each
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)
126 // *** DataRelations from annotations
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
130 // ** Informative References
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
137 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
147 // The above copyright notice and this permission notice shall be
148 // included in all copies or substantial portions of the Software.
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.
160 using System.Collections;
162 using System.Data.Common;
163 using System.Globalization;
165 using System.Xml.Schema;
168 namespace System.Data
170 internal class TableStructureCollection : CollectionBase
172 public void Add (TableStructure table)
177 public TableStructure this [int i] {
178 get { return List [i] as TableStructure; }
181 public TableStructure this [string name] {
183 foreach (TableStructure ts in List)
184 if (ts.Table.TableName == name)
191 internal class RelationStructureCollection : CollectionBase
193 public void Add (RelationStructure rel)
198 public RelationStructure this [int i] {
199 get { return List [i] as RelationStructure; }
202 public RelationStructure this [string parent, string child] {
204 foreach (RelationStructure rel in List)
205 if (rel.ParentTableName == parent && rel.ChildTableName == child)
212 internal class TableStructure
214 public TableStructure (DataTable table)
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;
226 public bool ContainsColumn (string name)
228 foreach (DataColumn col in NonOrdinalColumns)
229 if (col.ColumnName == name)
231 foreach (DataColumn col in OrdinalColumns.Keys)
232 if (col.ColumnName == name)
238 internal class RelationStructure
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;
249 internal class ConstraintStructure
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;
260 public ConstraintStructure (string tname, string [] cols, bool [] isAttr, string cname, bool isPK, string refName, bool isNested, bool isConstraintOnly)
264 IsAttribute = isAttr;
265 ConstraintName = XmlHelper.Decode (cname);
269 IsConstraintOnly = isConstraintOnly;
273 internal class XmlSchemaDataImporter
275 static readonly XmlSchemaDatatype schemaIntegerType;
276 static readonly XmlSchemaDatatype schemaDecimalType;
277 static readonly XmlSchemaComplexType schemaAnyType;
279 static XmlSchemaDataImporter ()
281 XmlSchema s = new XmlSchema ();
282 XmlSchemaAttribute a = new XmlSchemaAttribute ();
284 a.SchemaTypeName = new XmlQualifiedName ("integer", XmlSchema.Namespace);
286 XmlSchemaAttribute b = new XmlSchemaAttribute ();
288 b.SchemaTypeName = new XmlQualifiedName ("decimal", XmlSchema.Namespace);
290 XmlSchemaElement e = new XmlSchemaElement ();
295 schemaIntegerType = ((XmlSchemaSimpleType) a.AttributeSchemaType).Datatype;
296 schemaDecimalType = ((XmlSchemaSimpleType) b.AttributeSchemaType).Datatype;
297 schemaAnyType = e.ElementSchemaType as XmlSchemaComplexType;
299 schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
300 schemaDecimalType = b.AttributeType as XmlSchemaDatatype;
301 schemaAnyType = e.ElementType as XmlSchemaComplexType;
311 ArrayList relations = new ArrayList ();
312 Hashtable reservedConstraints = new Hashtable ();
314 // such element that has an attribute msdata:IsDataSet="true"
315 XmlSchemaElement datasetElement;
317 // choice alternatives in the "dataset element"
318 ArrayList topLevelElements = new ArrayList ();
320 // import target elements
321 ArrayList targetElements = new ArrayList ();
323 TableStructure currentTable;
326 // TODO: Do we need a collection here?
327 TableAdapterSchemaInfo currentAdapter;
333 public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
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);
346 internal TableAdapterSchemaInfo CurrentAdapter {
347 get { return currentAdapter; }
353 public void Process ()
355 if (schema.Id != null)
356 dataset.DataSetName = schema.Id; // default. Overridable by "DataSet element"
357 dataset.Namespace = schema.TargetNamespace;
359 // Find dataset element
360 foreach (XmlSchemaObject obj in schema.Items) {
361 XmlSchemaElement el = obj as XmlSchemaElement;
363 if (datasetElement == null &&
364 IsDataSetElement (el))
367 if (el.ElementSchemaType is XmlSchemaComplexType &&
368 el.ElementSchemaType != schemaAnyType)
370 if (el.ElementType is XmlSchemaComplexType &&
371 el.ElementType != schemaAnyType)
373 targetElements.Add (obj);
377 // make reservation of identity constraints
378 if (datasetElement != null) {
380 foreach (XmlSchemaObject obj in datasetElement.Constraints)
381 if (! (obj is XmlSchemaKeyref))
382 ReserveSelfIdentity ((XmlSchemaIdentityConstraint) obj);
384 foreach (XmlSchemaObject obj in datasetElement.Constraints)
385 if (obj is XmlSchemaKeyref)
386 ReserveRelationIdentity (datasetElement, (XmlSchemaKeyref) obj);
389 foreach (XmlSchemaObject obj in schema.Items) {
390 if (obj is XmlSchemaElement) {
391 XmlSchemaElement el = obj as XmlSchemaElement;
393 if (el.ElementSchemaType is XmlSchemaComplexType &&
394 el.ElementSchemaType != schemaAnyType)
396 if (el.ElementType is XmlSchemaComplexType &&
397 el.ElementType != schemaAnyType)
399 targetElements.Add (obj);
403 // This collection will grow up while processing elements.
404 int globalElementCount = targetElements.Count;
406 for (int i = 0; i < globalElementCount; i++)
407 ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
409 // Rest are local elements.
410 for (int i = globalElementCount; i < targetElements.Count; i++)
411 ProcessDataTableElement ((XmlSchemaElement) targetElements [i]);
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);
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);
425 foreach (XmlSchemaObject obj in datasetElement.Constraints)
426 if (obj is XmlSchemaKeyref)
427 ProcessRelationIdentity (datasetElement, reservedConstraints [obj] as ConstraintStructure);
430 foreach (RelationStructure rs in this.relations)
431 dataset.Relations.Add (GenerateRelationship (rs));
434 private bool IsDataSetElement (XmlSchemaElement el)
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
446 throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
452 if (schema.Elements.Count != 1)
454 if (!(el.SchemaType is XmlSchemaComplexType))
456 XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
457 if (ct.AttributeUses.Count > 0)
459 XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
460 if (gb == null || gb.Items.Count == 0)
462 foreach (XmlSchemaParticle p in gb.Items) {
463 if (ContainsColumn (p))
469 private bool ContainsColumn (XmlSchemaParticle p)
471 XmlSchemaElement el = p as XmlSchemaElement;
473 XmlSchemaComplexType ct = null;
475 ct = el.ElementSchemaType as XmlSchemaComplexType;
477 ct = el.ElementType as XmlSchemaComplexType;
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
486 return false; // table element
488 XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
489 for (int i = 0; i < gb.Items.Count; i++) {
490 if (ContainsColumn ((XmlSchemaParticle) gb.Items [i]))
496 private void ProcessGlobalElement (XmlSchemaElement el)
498 // If it is already registered (by resolving reference
499 // in previously-imported elements), just ignore.
500 if (dataset.Tables.Contains (el.QualifiedName.Name))
503 // If type is not complex, just skip this element
505 if (! (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType))
507 if (! (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType))
511 if (IsDataSetElement (el)) {
512 ProcessDataSetElement (el);
516 dataset.Locale = CultureInfo.CurrentCulture;
518 // Register as a top-level element
519 topLevelElements.Add (el);
520 // Create DataTable for this element
521 ProcessDataTableElement (el);
524 private void ProcessDataSetElement (XmlSchemaElement el)
526 dataset.DataSetName = el.Name;
527 this.datasetElement = el;
529 // Search for locale attributes
530 bool useCurrent = false;
531 if (el.UnhandledAttributes != null) {
532 foreach (XmlAttribute attr in el.UnhandledAttributes) {
534 if (attr.LocalName == "UseCurrentLocale" &&
535 attr.NamespaceURI == XmlConstants.MsdataNamespace)
538 if (attr.LocalName == "Locale" &&
539 attr.NamespaceURI == XmlConstants.MsdataNamespace) {
540 CultureInfo ci = new CultureInfo (attr.Value);
546 if (!useCurrent && !dataset.LocaleSpecified) // then set current culture instance _explicitly_
547 dataset.Locale = CultureInfo.CurrentCulture;
550 // Process content type particle (and create DataTable)
551 XmlSchemaComplexType ct = null;
553 ct = el.ElementSchemaType as XmlSchemaComplexType;
555 ct = el.ElementType as XmlSchemaComplexType;
557 XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
559 HandleDataSetContentTypeParticle (p);
562 private void HandleDataSetContentTypeParticle (XmlSchemaParticle p)
564 XmlSchemaElement el = p as XmlSchemaElement;
567 if (el.ElementSchemaType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
569 if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
571 ProcessDataTableElement (el);
573 else if (p is XmlSchemaGroupBase) {
574 foreach (XmlSchemaParticle pc in ((XmlSchemaGroupBase) p).Items)
575 HandleDataSetContentTypeParticle (pc);
579 private void ProcessDataTableElement (XmlSchemaElement el)
581 string tableName = XmlHelper.Decode (el.QualifiedName.Name);
582 // If it is already registered, just ignore.
583 if (dataset.Tables.Contains (tableName))
586 DataTable table = new DataTable (tableName);
587 table.Namespace = el.QualifiedName.Namespace;
588 TableStructure oldTable = currentTable;
589 currentTable = new TableStructure (table);
591 dataset.Tables.Add (table);
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);
602 // Handle complex type (NOTE: It is (or should be)
603 // impossible the type is other than complex type).
604 XmlSchemaComplexType ct = null;
606 ct = (XmlSchemaComplexType) el.ElementSchemaType;
608 ct = (XmlSchemaComplexType) el.ElementType;
612 foreach (DictionaryEntry de in ct.AttributeUses)
613 ImportColumnAttribute ((XmlSchemaAttribute) de.Value);
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.
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);
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);
647 currentTable = oldTable;
650 private DataRelation GenerateRelationship (RelationStructure rs)
652 DataTable ptab = dataset.Tables [rs.ParentTableName];
653 DataTable ctab = dataset.Tables [rs.ChildTableName];
656 string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
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);
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])];
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);
674 rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
676 DataColumn pcol = ptab.Columns [XmlHelper.Decode (rs.ParentColumnName)];
677 DataColumn ccol = ctab.Columns [XmlHelper.Decode (rs.ChildColumnName)];
679 ccol = CreateChildColumn (pcol, ctab);
680 rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
682 rel.Nested = rs.IsNested;
683 if (rs.CreateConstraint)
684 rel.ParentTable.PrimaryKey = rel.ParentColumns;
688 private DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
690 DataColumn col = childTable.Columns.Add (parentColumn.ColumnName,
691 parentColumn.DataType);
692 col.Namespace = String.Empty;
693 col.ColumnMapping = MappingType.Hidden;
697 private void ImportColumnGroupBase (XmlSchemaElement parent, XmlSchemaGroupBase gb)
699 foreach (XmlSchemaParticle p in gb.Items) {
700 XmlSchemaElement el = p as XmlSchemaElement;
702 ImportColumnElement (parent, el);
703 else if (p is XmlSchemaGroupBase)
704 ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
705 // otherwise p is xs:any
709 private XmlSchemaDatatype GetSchemaPrimitiveType (object type)
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;
719 // Note that this column might be Hidden
720 private void ImportColumnAttribute (XmlSchemaAttribute attr)
722 DataColumn col = new DataColumn ();
723 col.ColumnName = attr.QualifiedName.Name;
724 col.Namespace = attr.QualifiedName.Namespace;
725 XmlSchemaDatatype dt = null;
727 dt = GetSchemaPrimitiveType (((XmlSchemaSimpleType) attr.AttributeSchemaType).Datatype);
729 dt = GetSchemaPrimitiveType (attr.AttributeType);
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
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
741 if (attr.Use == XmlSchemaUse.Prohibited)
742 col.ColumnMapping = MappingType.Hidden;
744 col.ColumnMapping = MappingType.Attribute;
745 col.DefaultValue = GetAttributeDefaultValue (attr);
747 if (attr.Use == XmlSchemaUse.Required)
748 col.AllowDBNull = false;
751 FillFacet (col, attr.AttributeSchemaType as XmlSchemaSimpleType);
753 FillFacet (col, attr.AttributeType as XmlSchemaSimpleType);
756 // Call this method after filling the name
757 ImportColumnMetaInfo (attr, attr.QualifiedName, col);
761 private void ImportColumnElement (XmlSchemaElement parent, XmlSchemaElement el)
763 // FIXME: element nest check
765 DataColumn col = new DataColumn ();
766 col.DefaultValue = GetElementDefaultValue (el);
767 col.AllowDBNull = (el.MinOccurs == 0);
770 if (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType)
772 if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType)
774 FillDataColumnComplexElement (parent, el, col);
775 else if (el.MaxOccurs != 1)
776 FillDataColumnRepeatedSimpleElement (parent, el, col);
778 FillDataColumnSimpleElement (el, col);
781 // common process for element and attribute
782 private void ImportColumnMetaInfo (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
784 if (obj.UnhandledAttributes != null) {
785 foreach (XmlAttribute attr in obj.UnhandledAttributes) {
786 if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
788 switch (attr.LocalName) {
789 case XmlConstants.Caption:
790 col.Caption = attr.Value;
792 case XmlConstants.DataType:
793 col.DataType = Type.GetType (attr.Value);
795 case XmlConstants.AutoIncrement:
796 col.AutoIncrement = bool.Parse (attr.Value);
798 case XmlConstants.AutoIncrementSeed:
799 col.AutoIncrementSeed = int.Parse (attr.Value);
801 case XmlConstants.AutoIncrementStep:
802 col.AutoIncrementStep = int.Parse (attr.Value);
804 case XmlConstants.ReadOnly:
805 col.ReadOnly = XmlConvert.ToBoolean (attr.Value);
807 case XmlConstants.Ordinal:
808 int ordinal = int.Parse (attr.Value);
815 private void FillDataColumnComplexElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
817 if (targetElements.Contains (el))
818 return; // do nothing
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.");
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);
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;
842 // If the element is not referenced one, the element will be handled later.
843 if (el.RefName == XmlQualifiedName.Empty)
844 ProcessDataTableElement (el);
848 private bool DataSetDefinesKey (string name)
850 foreach (ConstraintStructure c in reservedConstraints.Values)
851 if (c.TableName == name && (c.IsPrimaryKey || c.IsNested))
856 private void AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
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));
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;
871 ImportColumnMetaInfo (el, el.QualifiedName, col);
875 // check name identity
876 string name = XmlHelper.Decode (parent.QualifiedName.Name) + "_Id";
878 while (currentTable.ContainsColumn (name))
879 name = String.Format ("{0}_{1}", name, count++);
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);
890 currentTable.PrimaryKey = col;
893 private void FillDataColumnRepeatedSimpleElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
895 if (targetElements.Contains (el))
896 return; // do nothing
898 AddParentKeyColumn (parent, el, col);
899 DataColumn pkey = currentTable.PrimaryKey;
901 string elName = XmlHelper.Decode (el.QualifiedName.Name);
902 string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
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);
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;
921 cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
923 cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
926 dt.Columns.Add (cc2);
928 dataset.Tables.Add (dt);
930 RelationStructure rel = new RelationStructure ();
931 rel.ParentTableName = parentName;
932 rel.ChildTableName = dt.TableName;
933 rel.ParentColumnName = pkey.ColumnName;
934 rel.ChildColumnName = cc.ColumnName;
936 rel.CreateConstraint = true;
940 private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
942 col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
943 col.Namespace = el.QualifiedName.Namespace;
944 col.ColumnMapping = MappingType.Element;
946 col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
947 FillFacet (col, el.ElementSchemaType as XmlSchemaSimpleType);
949 col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
950 FillFacet (col, el.ElementType as XmlSchemaSimpleType);
953 ImportColumnMetaInfo (el, el.QualifiedName, col);
958 private void AddColumn (DataColumn col)
961 currentTable.NonOrdinalColumns.Add (col);
963 currentTable.OrdinalColumns.Add (col, col.Ordinal);
966 private void FillFacet (DataColumn col, XmlSchemaSimpleType st)
968 if (st == null || st.Content == null)
971 // Handle restriction facets
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.");
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);
984 private Type ConvertDatatype (XmlSchemaDatatype dt)
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);
998 return typeof (ulong);
1001 return dt.ValueType;
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)
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.
1013 tableName = tableName.Substring (index + 1);
1015 // Round QName to NSName
1016 index = tableName.LastIndexOf (':');
1018 tableName = tableName.Substring (index + 1);
1020 return XmlHelper.Decode (tableName);
1023 private void ReserveSelfIdentity (XmlSchemaIdentityConstraint ic)
1025 string tableName = GetSelectorTarget (ic.Selector.XPath);
1027 string [] cols = new string [ic.Fields.Count];
1028 bool [] isAttrSpec = new bool [cols.Length];
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 (':');
1036 colName = colName.Substring (index + 1);
1038 colName = colName.Substring (1);
1040 colName = XmlHelper.Decode (colName);
1042 isAttrSpec [i] = isAttr;
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)
1054 switch (attr.LocalName) {
1055 case XmlConstants.ConstraintName:
1056 constraintName = attr.Value;
1058 case XmlConstants.PrimaryKey:
1059 isPK = bool.Parse(attr.Value);
1064 reservedConstraints.Add (ic,
1065 new ConstraintStructure (tableName, cols,
1066 isAttrSpec, constraintName, isPK, null, false, false));
1069 private void ProcessSelfIdentity (ConstraintStructure c)
1071 // Basic concept came from XmlSchemaMapper.cs
1073 string tableName = c.TableName;
1075 DataTable dt = dataset.Tables [tableName];
1078 throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
1080 // nonexistent table name. .NET ignores it for DataTable.ReadXmlSchema().
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];
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.");
1096 cols [i] = dt.Columns [colName];
1099 bool isPK = c.IsPrimaryKey;
1100 string constraintName = c.ConstraintName;
1101 dt.Constraints.Add (new UniqueConstraint (
1102 constraintName, cols, isPK));
1105 private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
1107 // Basic concept came from XmlSchemaMapper.cs
1109 string tableName = GetSelectorTarget (keyref.Selector.XPath);
1111 string [] cols = new string [keyref.Fields.Count];
1112 bool [] isAttrSpec = new bool [cols.Length];
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 (':');
1119 colName = colName.Substring (index + 1);
1121 colName = colName.Substring (1);
1123 colName = XmlHelper.Decode (colName);
1125 isAttrSpec [i] = isAttr;
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)
1135 switch (attr.LocalName) {
1136 case XmlConstants.ConstraintName:
1137 constraintName = attr.Value;
1139 case XmlConstants.IsNested:
1140 if (attr.Value == "true")
1143 case XmlConstants.ConstraintOnly:
1144 if (attr.Value == "true")
1145 isConstraintOnly = true;
1151 reservedConstraints.Add (keyref, new ConstraintStructure (
1152 tableName, cols, isAttrSpec, constraintName,
1153 false, keyref.Refer.Name, isNested, isConstraintOnly));
1156 private void ProcessRelationIdentity (XmlSchemaElement element, ConstraintStructure c)
1158 // Basic concept came from XmlSchemaMapper.cs
1160 string tableName = c.TableName;
1163 DataTable dt = dataset.Tables [tableName];
1165 throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
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.");
1178 string name = c.ReferName;
1179 // get the unique constraint for the releation
1180 UniqueConstraint uniq = FindConstraint (name, element);
1182 ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
1183 dt.Constraints.Add (fkc);
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);
1192 dataset.Relations.Add (rel);
1196 // get the unique constraint for the relation.
1197 // name - the name of the XmlSchemaUnique element
1198 private UniqueConstraint FindConstraint (string name, XmlSchemaElement element)
1200 // Copied from XmlSchemaMapper.cs
1202 // find the element in the constraint collection.
1203 foreach (XmlSchemaIdentityConstraint c in element.Constraints) {
1204 if (c is XmlSchemaKeyref)
1207 if (c.Name == name) {
1208 string tableName = GetSelectorTarget (c.Selector.XPath);
1210 // find the table in the dataset.
1211 DataTable dt = dataset.Tables [tableName];
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];
1223 throw new DataException ("Target identity constraint was not found: " + name);
1226 private void HandleAnnotations (XmlSchemaAnnotation an, bool nested)
1228 foreach (XmlSchemaObject content in an.Items) {
1229 XmlSchemaAppInfo ai = content as XmlSchemaAppInfo;
1231 foreach (XmlNode n in ai.Markup) {
1232 XmlElement el = n as XmlElement;
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);
1239 if (el != null && el.LocalName == "DataSource" && el.NamespaceURI == XmlConstants.MsdatasourceNamespace)
1240 HandleDataSourceAnnotation (el, nested);
1248 private void HandleDataSourceAnnotation (XmlElement el, bool nested)
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
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, firstChild;
1262 foreach (XmlNode n in el.ChildNodes) {
1263 e = n as XmlElement;
1269 if (e.LocalName == "Connections" && (firstChild = e.FirstChild as XmlElement) != null) {
1270 providerName = firstChild.GetAttribute ("Provider");
1271 connString = firstChild.GetAttribute ("AppSettingsPropertyName");
1272 provider = DbProviderFactories.GetFactory (providerName);
1275 #endif // !MONOTOUCH
1276 // #325464 debugging
1277 //Console.WriteLine ("ProviderName: " + providerName + "Connstr: " + connString);
1279 if (e.LocalName == "Tables")
1283 if (tablesElement != null && provider != null) {
1284 foreach (XmlNode node in tablesElement.ChildNodes) {
1285 ProcessTableAdapter (node as XmlElement, provider, connString);
1290 private void ProcessTableAdapter (XmlElement el, DbProviderFactory provider, string connStr)
1293 string datasetTableName = null;
1298 // #325464 debugging
1299 //Console.WriteLine ("in ProcessTableAdapters...");
1300 currentAdapter = new TableAdapterSchemaInfo (provider);
1301 currentAdapter.ConnectionString = connStr;
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");
1309 if (String.IsNullOrEmpty (currentAdapter.Name))
1310 currentAdapter.Name = el.GetAttribute ("DataAccessorName");
1312 //Console.WriteLine ("Name: "+currentAdapter.Name);
1313 foreach (XmlNode n in el.ChildNodes) {
1314 e = n as XmlElement;
1316 //Console.WriteLine ("Children of Tables: "+e.LocalName);
1320 switch (e.LocalName) {
1323 foreach (XmlNode msn in e.ChildNodes)
1324 ProcessDbSource (msn as XmlElement);
1328 DataTableMapping tableMapping = new DataTableMapping ();
1329 tableMapping.SourceTable = "Table";
1330 tableMapping.DataSetTable = datasetTableName;
1332 foreach (XmlNode mps in e.ChildNodes)
1333 ProcessColumnMapping (mps as XmlElement, tableMapping);
1335 currentAdapter.Adapter.TableMappings.Add (tableMapping);
1341 private void ProcessDbSource (XmlElement el)
1351 //Console.WriteLine ("ProcessDbSources: "+el.LocalName);
1353 tmp = el.GetAttribute ("GenerateShortCommands");
1354 //Console.WriteLine ("GenerateShortCommands: {0}", tmp);
1355 if (!String.IsNullOrEmpty (tmp))
1356 currentAdapter.ShortCommands = Convert.ToBoolean (tmp);
1358 DbCommandInfo cmdInfo = new DbCommandInfo ();
1359 tmp = el.GetAttribute ("GenerateMethods");
1360 if (!String.IsNullOrEmpty (tmp)) {
1361 DbSourceMethodInfo mthdInfo = null;
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;
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;
1390 case GenerateMethodsType.Both:
1391 mthdInfo = new DbSourceMethodInfo ();
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;
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;
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;
1430 foreach (XmlNode n in el.ChildNodes) {
1431 e = n as XmlElement;
1436 switch (e.LocalName) {
1437 case "SelectCommand":
1438 cmdInfo.Command = ProcessDbCommand (e.FirstChild as XmlElement);
1439 currentAdapter.Commands.Add (cmdInfo);
1441 case "InsertCommand":
1442 currentAdapter.Adapter.InsertCommand = ProcessDbCommand (e.FirstChild as XmlElement);
1444 case "UpdateCommand":
1445 currentAdapter.Adapter.UpdateCommand = ProcessDbCommand (e.FirstChild as XmlElement);
1447 case "DeleteCommand":
1448 currentAdapter.Adapter.DeleteCommand = ProcessDbCommand (e.FirstChild as XmlElement);
1454 private DbCommand ProcessDbCommand (XmlElement el)
1457 //Console.WriteLine (el.LocalName);
1458 string cmdText = null;
1459 string cmdType = null;
1460 ArrayList parameters = null;
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);
1474 DbCommand cmd = currentAdapter.Provider.CreateCommand ();
1475 cmd.CommandText = cmdText;
1476 if (cmdType == "StoredProcedure")
1477 cmd.CommandType = CommandType.StoredProcedure;
1479 cmd.CommandType = CommandType.Text;
1481 if (parameters != null)
1482 cmd.Parameters.AddRange (parameters.ToArray ());
1484 //Console.WriteLine ("Parameters count: {0}", cmd.Parameters.Count);
1488 private ArrayList ProcessDbParameters (XmlElement el)
1490 //Console.WriteLine ("ProcessDbParameters: "+el.LocalName);
1493 DbParameter param = null;
1494 ArrayList parameters = new ArrayList ();
1499 foreach (XmlNode n in el.ChildNodes) {
1500 e = n as XmlElement;
1504 param = currentAdapter.Provider.CreateParameter ();
1506 tmp = e.GetAttribute ("AllowDbNull");
1507 if (!String.IsNullOrEmpty (tmp))
1508 param.IsNullable = Convert.ToBoolean (tmp);
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;
1516 tmp = e.GetAttribute ("Direction");
1517 param.Direction = (ParameterDirection) Enum.Parse (typeof (ParameterDirection), tmp);
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");
1524 tmp = e.GetAttribute ("SourceColumnNullMapping");
1525 if (!String.IsNullOrEmpty (tmp))
1526 param.SourceColumnNullMapping = Convert.ToBoolean (tmp);
1528 tmp = e.GetAttribute ("SourceVersion");
1529 param.SourceVersion = (DataRowVersion) Enum.Parse (typeof (DataRowVersion), tmp);
1530 parameters.Add (param);
1536 private void ProcessColumnMapping (XmlElement el, DataTableMapping tableMapping)
1541 tableMapping.ColumnMappings.Add (el.GetAttribute ("SourceColumn"),
1542 el.GetAttribute ("DataSetColumn"));
1547 private void HandleRelationshipAnnotation (XmlElement el, bool nested)
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);
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);
1568 private object GetElementDefaultValue (XmlSchemaElement elem)
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
1576 return referenced.DefaultValue;
1579 private object GetAttributeDefaultValue (XmlSchemaAttribute attr)
1581 #if BUGGY_MS_COMPATIBLE
1584 else if (attr.RefName != XmlQualifiedName.Empty) {
1585 XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
1586 if (referenced != null)
1587 return referenced.DefaultValue;
1591 if (attr.DefaultValue != null)
1592 return attr.DefaultValue;
1593 return attr.FixedValue;
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)
1601 XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
1602 if (referenced == null) // considering missing sub components
1604 if (referenced.DefaultValue != null)
1605 return referenced.DefaultValue;
1606 return referenced.FixedValue;