2 // mcs/class/System.Data/System.Data/XmlSchemaMapper.cs
4 // Purpose: Maps XmlSchema to DataSet
6 // class: XmlSchemaMapper
7 // assembly: System.Data.dll
8 // namespace: System.Data
11 // Ville Palo <vi64pa@koti.soon.fi>
13 // (C) 2002 Ville Palo
20 using System.Xml.Schema;
21 using System.Collections;
22 using System.Globalization;
24 namespace System.Data {
26 internal class XmlSchemaMapper
31 private DataTable table;
32 enum ElementType {ELEMENT_UNDEFINED, ELEMENT_TABLE, ELEMENT_COLUMN};
33 private Hashtable TypeCollection = new Hashtable ();
34 private Hashtable ElementCollection = new Hashtable ();
40 public XmlSchemaMapper (DataSet dataset)
45 public XmlSchemaMapper (DataTable datatable)
50 #endregion // Constructors
52 #region Public methods
54 public void Read (XmlReader Reader)
56 XmlSchema Schema = XmlSchema.Read (Reader, new ValidationEventHandler (OnXmlSchemaValidation));
57 if (DSet != null) DSet.Namespace = Schema.TargetNamespace;
60 foreach (XmlSchemaObject Item in Schema.Items)
61 ReadXmlSchemaItem (Item);
64 #endregion // Public methods
66 #region Private methods
68 private void ReadXmlSchemaItem (XmlSchemaObject Item)
70 XmlSchemaObject SchemaObject;
72 if (Item is XmlSchemaType)
73 ReadXmlSchemaType ((XmlSchemaType)Item);
74 else if (Item is XmlSchemaElement)
75 ReadXmlSchemaElement (Item as XmlSchemaElement, ElementType.ELEMENT_UNDEFINED);
78 private void ReadXmlSchemaSequence (XmlSchemaSequence Sequence)
80 ReadXmlSchemaSequence (Sequence, null);
83 private void ReadXmlSchemaSequence (XmlSchemaSequence Sequence, DataTable Table)
85 foreach (XmlSchemaObject TempObj in Sequence.Items) {
86 if (TempObj is XmlSchemaElement){
87 XmlSchemaElement schemaElement = (XmlSchemaElement)TempObj;
88 // the element can be a Column or a Table
89 // tables do not have a type.
90 if (schemaElement.SchemaTypeName.Name.Length > 0 || (schemaElement.SchemaType is XmlSchemaSimpleType))
91 ReadXmlSchemaElement (schemaElement, ElementType.ELEMENT_COLUMN, Table);
93 ReadXmlSchemaElement (schemaElement, ElementType.ELEMENT_TABLE, Table);
98 private void ReadXmlSchemaChoice (XmlSchemaChoice Choice)
100 XmlSchemaObject SchemaObject;
101 foreach (XmlSchemaObject TempObject in Choice.Items) {
102 if ((SchemaObject = TempObject as XmlSchemaElement) != null)
103 ReadXmlSchemaElement ((XmlSchemaElement)SchemaObject, ElementType.ELEMENT_TABLE);
107 private void ReadXmlSchemaElement (XmlSchemaElement Element)
109 ReadXmlSchemaElement (Element, ElementType.ELEMENT_UNDEFINED);
112 private void ReadXmlSchemaElement (XmlSchemaElement Element, ElementType ElType)
114 ReadXmlSchemaElement (Element, ElType, null);
117 private void ReadXmlSchemaElement (XmlSchemaElement Element, ElementType ElType, DataTable Table)
119 Hashtable Attributes = ReadUnhandledAttributes (Element.UnhandledAttributes);
120 DataTable Table2 = null;
122 if (Attributes.Contains (XmlConstants.IsDataSet)) { // DataSet -elemt
124 if (String.Compare (Attributes [XmlConstants.IsDataSet].ToString (), "true", true) == 0 && DSet != null)
125 DSet.DataSetName = Element.Name;
127 if (Attributes.Contains (XmlConstants.Locale)) {
128 CultureInfo cinfo = new CultureInfo((String)Attributes [XmlConstants.Locale]);
129 if (DSet != null) DSet.Locale = cinfo;
130 else table.Locale = cinfo;
133 else if (Element.SchemaTypeName != null && Element.SchemaTypeName.Namespace != XmlConstants.SchemaNamespace
134 && Element.SchemaTypeName.Name != String.Empty) {
137 // If type is not standard type
140 if (DSet == null) throw new InvalidOperationException ("Schema not valid for a DataTable");
142 DataTable TempTable = new DataTable (Element.Name);
143 DSet.Tables.Add (TempTable);
145 // If type is already defined in schema read it...
146 if (TypeCollection.Contains (Element.SchemaTypeName.ToString ()))
147 ReadXmlSchemaType ((XmlSchemaType)TypeCollection [Element.SchemaTypeName.ToString ()], TempTable);
148 else // but if it's not yet defined put it safe to wait if we need it later.
149 ElementCollection.Add (Element.SchemaTypeName.Name, TempTable);
152 else if (Element.RefName != null && Element.RefName.Name != string.Empty) { // if there is a ref=
154 if (ElementCollection.Contains (Element.RefName.Name))
155 ReadXmlSchemaElement ((XmlSchemaElement)ElementCollection [Element.RefName.Name], ElementType.ELEMENT_TABLE);
157 else if (ElementType.ELEMENT_UNDEFINED != ElType) {
159 if (ElType == ElementType.ELEMENT_TABLE){
161 // we have to return else all child element of the table will be computed again.
164 else if (ElType == ElementType.ELEMENT_COLUMN && Table != null)
165 ReadColumn (Element, Table);
168 // this element is undefined, for now
169 ElementCollection.Add (Element.Name, Element);
173 if (Element.SchemaType != null)
174 ReadXmlSchemaType (Element.SchemaType);
176 // Read possible constraints
177 if (Element.Constraints != null && Element.Constraints.Count > 0){
178 ReadXmlSchemaConstraints (Element.Constraints);
182 private void ReadTable (XmlSchemaElement Element)
184 DataTable TempTable = null;
186 // Add the table to the DataSet only if it is not already in there.
188 if (DSet.Tables.Contains (Element.Name)) return;
189 TempTable = new DataTable (Element.Name);
190 DSet.Tables.Add (TempTable);
193 if (table.TableName.Length != 0)
194 throw new InvalidOperationException ("More than one table is defined in this schema");
195 table.TableName = Element.Name;
198 ReadXmlSchemaType (Element.SchemaType, TempTable);
201 private void ReadColumn (XmlSchemaElement Element, DataTable Table)
203 DataColumn Column = new DataColumn (Element.Name);
204 Column.DataType = GetColumnType(Element.SchemaTypeName.Name);
205 Table.Columns.Add (Column);
207 if (Element.UnhandledAttributes != null) {
209 foreach (XmlAttribute Attr in Element.UnhandledAttributes) {
210 switch (Attr.LocalName) {
212 case XmlConstants.Caption:
213 Column.Caption = Attr.Value;
215 case XmlConstants.DataType:
216 Column.DataType = Type.GetType (Attr.Value);
218 case XmlConstants.AutoIncrement:
219 Column.AutoIncrement = bool.Parse(Attr.Value);
221 case XmlConstants.AutoIncrementSeed:
222 Column.AutoIncrementSeed = int.Parse(Attr.Value);
231 // Handel rest of the parameters
234 if (Column.DataType == null)
235 Column.DataType = Type.GetType ("System.String");
237 if (Element.DefaultValue != null)
238 Column.DefaultValue = Element.DefaultValue;
240 // If Element have type
241 if (Element.SchemaType != null)
242 ReadXmlSchemaType (Element.SchemaType, Column);
246 private Type GetColumnType (String typeName)
248 if (typeName == null || typeName.Length == 0)
249 return typeof (string);
261 case "unsignedByte" :
270 case "usignedShort" :
276 case "unsignedLong" :
289 t = typeof (decimal);
292 t = typeof (DateTime);
295 t = typeof (TimeSpan);
297 case "base64Binary" :
308 // Makes new Hashtable of the attributes.
309 private Hashtable ReadUnhandledAttributes (XmlAttribute [] Attributes)
311 Hashtable Result = new Hashtable ();
313 if (Attributes == null)
316 foreach (XmlAttribute attribute in Attributes) {
317 Result.Add (attribute.LocalName, attribute.Value);
323 private void ReadXmlSchemaConstraints (XmlSchemaObjectCollection Constraints)
325 foreach (XmlSchemaObject Constraint in Constraints) {
326 if (Constraint is XmlSchemaUnique)
327 ReadXmlSchemaUnique ((XmlSchemaUnique)Constraint);
328 if (Constraint is XmlSchemaKeyref)
329 ReadXmlSchemaKeyref ((XmlSchemaKeyref)Constraint, Constraints);
334 private void ReadXmlSchemaUnique (XmlSchemaUnique Unique)
336 // FIXME: Parsing XPath
337 string TableName = Unique.Selector.XPath;
338 int index = TableName.IndexOf(':');
340 TableName = TableName.Substring (index + 1);
341 else if(TableName.StartsWith (".//"))
342 TableName = TableName.Substring (3);
344 DataColumn [] Columns;
345 DataTable Table = GetTable (TableName);
347 Columns = new DataColumn [Unique.Fields.Count];
349 foreach (XmlSchemaXPath Field in Unique.Fields) {
350 string columnName = Field.XPath;
351 index = columnName.IndexOf (':');
353 columnName = columnName.Substring (index + 1);
354 if (Table.Columns.Contains (columnName)) {
355 Columns [i] = Table.Columns [columnName];
361 // find if there is an attribute with the constraint name
362 // if not use the XmlSchemaUnique name.
363 string constraintName = Unique.Name;
364 if (Unique.UnhandledAttributes != null){
365 foreach (XmlAttribute attr in Unique.UnhandledAttributes){
366 if (attr.LocalName == "ConstraintName"){
367 constraintName = attr.Value;
369 else if (attr.LocalName == XmlConstants.PrimaryKey){
370 isPK = bool.Parse(attr.Value);
375 UniqueConstraint Constraint = new UniqueConstraint (constraintName, Columns, isPK);
376 Table.Constraints.Add (Constraint);
381 private void ReadXmlSchemaKeyref (XmlSchemaKeyref KeyRef, XmlSchemaObjectCollection collection) {
383 if (DSet == null) return; // Ignore relations for table-only schemas
385 string TableName = KeyRef.Selector.XPath;
386 int index = TableName.IndexOf(':');
388 TableName = TableName.Substring (index + 1);
389 else if (TableName.StartsWith (".//"))
390 TableName = TableName.Substring (3);
391 DataColumn [] Columns;
392 DataTable Table = GetTable (TableName);
394 Columns = new DataColumn [KeyRef.Fields.Count];
396 foreach (XmlSchemaXPath Field in KeyRef.Fields) {
397 string columnName = Field.XPath;
398 index = columnName.IndexOf (':');
400 columnName = columnName.Substring (index + 1);
401 if (Table.Columns.Contains (columnName)) {
402 Columns [i] = Table.Columns [columnName];
406 string name = KeyRef.Refer.Name;
407 // get the unique constraint for the releation
408 UniqueConstraint constraint = GetDSConstraint(name, collection);
410 ForeignKeyConstraint fkConstraint = new ForeignKeyConstraint(constraint.Columns, Columns);
411 Table.Constraints.Add (fkConstraint);
412 // generate the relation.
413 DataRelation relation = new DataRelation(KeyRef.Name, constraint.Columns, Columns, false);
414 if (KeyRef.UnhandledAttributes != null){
415 foreach (XmlAttribute attr in KeyRef.UnhandledAttributes){
416 if (attr.LocalName == "IsNested"){
417 if (attr.Value == "true")
418 relation.Nested = true;
423 DSet.Relations.Add(relation);
427 // get the unique constraint for the relation.
428 // name - the name of the XmlSchemaUnique element
429 private UniqueConstraint GetDSConstraint(string name, XmlSchemaObjectCollection collection)
431 // find the element in the constraint collection.
432 foreach (XmlSchemaObject shemaObj in collection){
433 if (shemaObj is XmlSchemaUnique){
434 XmlSchemaUnique unique = (XmlSchemaUnique) shemaObj;
435 if (unique.Name == name){
436 string tableName = unique.Selector.XPath;
437 int index = tableName.IndexOf (':');
439 tableName = tableName.Substring (index + 1);
440 else if (tableName.StartsWith (".//"))
441 tableName = tableName.Substring (3);
443 // find the table in the dataset.
444 DataTable table = GetTable (tableName);
447 string constraintName = unique.Name;
448 // find if there is an attribute with the constraint name
449 // if not use the XmlSchemaUnique name.
450 if (unique.UnhandledAttributes != null){
451 foreach (XmlAttribute attr in unique.UnhandledAttributes){
452 if (attr.LocalName == "ConstraintName"){
453 constraintName = attr.Value;
458 if (table.Constraints.Contains(constraintName))
459 return (UniqueConstraint)table.Constraints[constraintName];
469 #endregion // Private methods
471 #region Private listeners
473 private void OnXmlSchemaValidation (object sender, ValidationEventArgs args)
478 #endregion // Private listeners
480 #region Private TypeReaders
482 // Reads XmlSchemaType
483 private void ReadXmlSchemaType (XmlSchemaType SchemaType)
485 ReadXmlSchemaType (SchemaType, (DataTable)null);
488 // Reads XmlSchemaType and decides is it Complex or Simple and continue reading those types
489 private void ReadXmlSchemaType (XmlSchemaType SchemaType, DataTable Table)
491 if (SchemaType is XmlSchemaComplexType)
492 ReadXmlSchemaComplexType ((XmlSchemaComplexType)SchemaType, Table);
493 else if (SchemaType is XmlSchemaSimpleType)
494 ReadXmlSchemaSimpleType ((XmlSchemaSimpleType)SchemaType, Table);
497 // Same as above but with DataColumn
498 private void ReadXmlSchemaType (XmlSchemaType SchemaType, DataColumn Column)
500 if (SchemaType is XmlSchemaComplexType)
501 ReadXmlSchemaComplexType ((XmlSchemaComplexType)SchemaType, Column);
502 else if (SchemaType is XmlSchemaSimpleType)
503 ReadXmlSchemaSimpleType ((XmlSchemaSimpleType)SchemaType, Column);
506 #endregion // PrivateTypeReader
508 #region TypeReaderHelppers
510 private void ReadXmlSchemaSimpleType (XmlSchemaSimpleType SimpleType, DataColumn Column)
513 if (SimpleType.Content is XmlSchemaSimpleTypeRestriction)
514 ReadXmlSchemaSimpleTypeRestriction ((XmlSchemaSimpleTypeRestriction)SimpleType.Content, Column);
518 private void ReadXmlSchemaSimpleType (XmlSchemaSimpleType SimpleType, DataTable Table)
520 // TODO: Is it possible that Table-element have simpletype???
524 private void ReadXmlSchemaSimpleTypeRestriction (XmlSchemaSimpleTypeRestriction Restriction, DataColumn Column)
526 foreach (XmlSchemaObject Facet in Restriction.Facets) {
528 // FIXME: I dont know are everyone of these needed but, let them be here for now
529 if (Facet is XmlSchemaMaxLengthFacet)
530 Column.MaxLength = Int32.Parse(((XmlSchemaFacet)Facet).Value);
531 //else if (Facet is XmlSchemaMinLengthFacet)
533 //else if (Facet is XmlSchemaLengthFacet)
535 //else if (Facet is XmlSchemaPatternFacet)
537 //else if (Facet is XmlSchemaEnumerationFacet)
539 //else if (Facet is XmlSchemaMaxInclusiveFacet)
541 //else if (Facet is XmlSchemaMaxExclusiveFacet)
543 //else if (Facet is XmlSchemaMinInclusiveFacet)
545 //else if (Facet is XmlSchemaMinExclusiveFacet)
547 //else if (Facet is XmlSchemaFractionDigitsFacet)
549 //else if (Facet is XmlSchemaTotalDigitsFacet)
551 //else if (Facet is XmlSchemaWhiteSpaceFacet)
557 private void ReadXmlSchemaComplexType (XmlSchemaComplexType Type, DataColumn Column)
559 // TODO: is it possible that column-element have complextype
562 // Reads XmlSchemaComplexType with DataTable
563 private void ReadXmlSchemaComplexType (XmlSchemaComplexType Type, DataTable Table)
565 XmlSchemaComplexType ComplexType = Type as XmlSchemaComplexType;
567 if (ComplexType.Name != null && ComplexType.Name != string.Empty) {
569 if (ElementCollection.Contains (ComplexType.Name)) {
571 if (ComplexType.Particle is XmlSchemaChoice) {
572 ReadXmlSchemaChoice (ComplexType.Particle as XmlSchemaChoice);
574 else if (ComplexType.Particle is XmlSchemaSequence) {
576 DataTable TempTable = ElementCollection [ComplexType.Name] as DataTable;
577 ElementCollection.Remove (ComplexType.Name);
578 ReadXmlSchemaSequence (ComplexType.Particle as XmlSchemaSequence, TempTable);
581 else if (ComplexType.Name != null && !TypeCollection.Contains (ComplexType.Name)) {
582 TypeCollection.Add (ComplexType.Name, ComplexType);
586 // If we are here it means that types of elements are Tables :-P
587 if (ComplexType.Particle is XmlSchemaSequence)
588 ReadXmlSchemaSequence (ComplexType.Particle as XmlSchemaSequence, Table);
593 XmlSchemaParticle Particle;
594 if ((Particle = ComplexType.Particle as XmlSchemaChoice) != null) {
595 ReadXmlSchemaChoice (Particle as XmlSchemaChoice);
597 else if ((Particle = ComplexType.Particle as XmlSchemaSequence) != null) {
598 ReadXmlSchemaSequence (Particle as XmlSchemaSequence, Table);
601 // read columns if they were written as simplecontent.
602 XmlSchemaSimpleContent simpleContent;
603 if ((simpleContent = ComplexType.ContentModel as XmlSchemaSimpleContent) != null){
604 ReadColumn (simpleContent, Table);
607 // read columns if they were written as attributes
608 if (ComplexType.Attributes != null) {
610 foreach (XmlSchemaObject sobj in ComplexType.Attributes) {
611 if (sobj is XmlSchemaAttribute)
612 ReadColumn ((XmlSchemaAttribute)sobj, Table);
618 private void ReadColumn (XmlSchemaSimpleContent simpleContent, DataTable Table)
620 DataColumn Column = new DataColumn ();
623 if (simpleContent.UnhandledAttributes != null) {
625 foreach (XmlAttribute Attr in simpleContent.UnhandledAttributes) {
626 switch (Attr.LocalName) {
628 case XmlConstants.ColumnName:
629 Column.ColumnName = Attr.Value;
638 XmlSchemaSimpleContentExtension extension;
\r
639 if ((extension = simpleContent.Content as XmlSchemaSimpleContentExtension) != null) {
\r
640 Column.DataType = GetColumnType (extension.BaseTypeName.Name);
\r
643 Table.Columns.Add (Column);
646 private void ReadColumn (XmlSchemaAttribute schemaAttribute, DataTable table)
648 DataColumn column = new DataColumn (schemaAttribute.Name);
649 column.DataType = GetColumnType (schemaAttribute.SchemaTypeName.Name);
650 table.Columns.Add (column);
653 DataTable GetTable (string name)
656 return DSet.Tables [name];
657 else if (name == table.TableName)
660 throw new InvalidOperationException ("Schema not valid for table '" + table.TableName + "'");
663 #endregion // TypeReaderHelppers