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 enum ElementType {ELEMENT_UNDEFINED, ELEMENT_TABLE, ELEMENT_COLUMN};
32 private Hashtable TypeCollection = new Hashtable ();
33 private Hashtable ElementCollection = new Hashtable ();
39 public XmlSchemaMapper (DataSet dataset)
44 #endregion // Constructors
46 #region Public methods
48 public void Read (XmlReader Reader)
50 XmlSchema Schema = XmlSchema.Read (Reader, new ValidationEventHandler (OnXmlSchemaValidation));
53 foreach (XmlSchemaObject Item in Schema.Items)
54 ReadXmlSchemaItem (Item);
57 #endregion // Public methods
59 #region Private methods
61 private void ReadXmlSchemaItem (XmlSchemaObject Item)
63 XmlSchemaObject SchemaObject;
65 if (Item is XmlSchemaType)
66 ReadXmlSchemaType ((XmlSchemaType)Item);
67 else if (Item is XmlSchemaElement)
68 ReadXmlSchemaElement (Item as XmlSchemaElement, ElementType.ELEMENT_UNDEFINED);
71 private void ReadXmlSchemaSequence (XmlSchemaSequence Sequence)
73 ReadXmlSchemaSequence (Sequence, null);
76 private void ReadXmlSchemaSequence (XmlSchemaSequence Sequence, DataTable Table)
78 foreach (XmlSchemaObject TempObj in Sequence.Items) {
79 if (TempObj is XmlSchemaElement){
80 XmlSchemaElement schemaElement = (XmlSchemaElement)TempObj;
81 // the element can be a Column or a Table
82 // tables do not have a type.
83 if (schemaElement.SchemaTypeName.Name.Length == 0)
84 ReadXmlSchemaElement (schemaElement, ElementType.ELEMENT_TABLE, Table);
86 ReadXmlSchemaElement (schemaElement, ElementType.ELEMENT_COLUMN, Table);
91 private void ReadXmlSchemaChoice (XmlSchemaChoice Choice)
93 XmlSchemaObject SchemaObject;
94 foreach (XmlSchemaObject TempObject in Choice.Items) {
95 if ((SchemaObject = TempObject as XmlSchemaElement) != null)
96 ReadXmlSchemaElement ((XmlSchemaElement)SchemaObject, ElementType.ELEMENT_TABLE);
100 private void ReadXmlSchemaElement (XmlSchemaElement Element)
102 ReadXmlSchemaElement (Element, ElementType.ELEMENT_UNDEFINED);
105 private void ReadXmlSchemaElement (XmlSchemaElement Element, ElementType ElType)
107 ReadXmlSchemaElement (Element, ElType, null);
110 private void ReadXmlSchemaElement (XmlSchemaElement Element, ElementType ElType, DataTable Table)
112 Hashtable Attributes = ReadUnhandledAttributes (Element.UnhandledAttributes);
113 DataTable Table2 = null;
115 if (Attributes.Contains ("IsDataSet")) { // DataSet -elemt
116 if (String.Compare (Attributes ["IsDataSet"].ToString (), "true", true) == 0)
117 DSet.DataSetName = Element.Name;
119 else if (Element.SchemaTypeName != null && Element.SchemaTypeName.Namespace != XmlConstants.SchemaNamespace
120 && Element.SchemaTypeName.Name != String.Empty) {
123 // If type is not standard type
126 DataTable TempTable = new DataTable (Element.Name);
127 DSet.Tables.Add (TempTable);
129 // If type is already defined in schema read it...
130 if (TypeCollection.Contains (Element.SchemaTypeName.ToString ()))
131 ReadXmlSchemaType ((XmlSchemaType)TypeCollection [Element.SchemaTypeName.ToString ()], TempTable);
132 else // but if it's not yet defined put it safe to wait if we need it later.
133 ElementCollection.Add (Element.SchemaTypeName.Name, TempTable);
136 else if (Element.RefName != null && Element.RefName.Name != string.Empty) { // if there is a ref=
138 if (ElementCollection.Contains (Element.RefName.Name))
139 ReadXmlSchemaElement ((XmlSchemaElement)ElementCollection [Element.RefName.Name], ElementType.ELEMENT_TABLE);
141 else if (ElementType.ELEMENT_UNDEFINED != ElType) {
143 if (ElType == ElementType.ELEMENT_TABLE){
145 // we have to return else all child element of the table will be computed again.
148 else if (ElType == ElementType.ELEMENT_COLUMN && Table != null)
149 ReadColumn (Element, Table);
152 // this element is undefined, for now
153 ElementCollection.Add (Element.Name, Element);
157 if (Element.SchemaType != null)
158 ReadXmlSchemaType (Element.SchemaType);
160 // Read possible constraints
161 if (Element.Constraints != null && Element.Constraints.Count > 0){
162 ReadXmlSchemaConstraints (Element.Constraints);
166 private void ReadTable (XmlSchemaElement Element)
168 DataTable TempTable = null;
169 // Add the table to the DataSet only if it is not already in there.
170 if (!DSet.Tables.Contains(Element.Name)) {
171 TempTable = new DataTable (Element.Name);
172 DSet.Tables.Add (TempTable);
173 ReadXmlSchemaType (Element.SchemaType, TempTable);
177 private void ReadColumn (XmlSchemaElement Element, DataTable Table)
179 DataColumn Column = new DataColumn (Element.Name);
180 Table.Columns.Add (Column);
182 if (Element.UnhandledAttributes != null) {
184 foreach (XmlAttribute Attr in Element.UnhandledAttributes) {
186 switch (Attr.LocalName) {
189 Column.Caption = Attr.Value;
192 Column.DataType = Type.GetType (Attr.Value);
204 // Handel rest of the parameters
207 if (Column.DataType == null)
208 Column.DataType = Type.GetType ("System.String");
210 if (Element.DefaultValue != null)
211 Column.DefaultValue = Element.DefaultValue;
213 // If Element have type
214 if (Element.SchemaType != null)
215 ReadXmlSchemaType (Element.SchemaType, Column);
218 // Makes new Hashtable of the attributes.
219 private Hashtable ReadUnhandledAttributes (XmlAttribute [] Attributes)
221 Hashtable Result = new Hashtable ();
223 if (Attributes == null)
226 foreach (XmlAttribute attribute in Attributes) {
227 Result.Add (attribute.LocalName, attribute.Value);
233 private void ReadXmlSchemaConstraints (XmlSchemaObjectCollection Constraints)
235 foreach (XmlSchemaObject Constraint in Constraints) {
236 if (Constraint is XmlSchemaUnique)
237 ReadXmlSchemaUnique ((XmlSchemaUnique)Constraint);
238 if (Constraint is XmlSchemaKeyref)
239 ReadXmlSchemaKeyref ((XmlSchemaKeyref)Constraint, Constraints);
244 private void ReadXmlSchemaUnique (XmlSchemaUnique Unique)
246 // FIXME: Parsing XPath
247 string TableName = Unique.Selector.XPath;
248 if (TableName.StartsWith (".//"))
249 TableName = TableName.Substring (3);
250 DataColumn [] Columns;
251 if (DSet.Tables.Contains (TableName)) {
252 DataTable Table = DSet.Tables [TableName];
253 Columns = new DataColumn [Unique.Fields.Count];
255 foreach (XmlSchemaXPath Field in Unique.Fields) {
256 if (Table.Columns.Contains (Field.XPath)) {
257 Table.Columns [Field.XPath].Unique = true;
258 Columns [i] = Table.Columns [Field.XPath];
263 // find if there is an attribute with the constraint name
264 // if not use the XmlSchemaUnique name.
265 string constraintName = Unique.Name;
266 if (Unique.UnhandledAttributes != null){
267 foreach (XmlAttribute attr in Unique.UnhandledAttributes){
268 if (attr.LocalName == "ConstraintName"){
269 constraintName = attr.Value;
274 UniqueConstraint Constraint = new UniqueConstraint (constraintName, Columns);
279 private void ReadXmlSchemaKeyref (XmlSchemaKeyref KeyRef, XmlSchemaObjectCollection collection) {
281 string TableName = KeyRef.Selector.XPath;
282 if (TableName.StartsWith (".//"))
283 TableName = TableName.Substring (3);
284 DataColumn [] Columns;
285 if (DSet.Tables.Contains (TableName)) {
286 DataTable Table = DSet.Tables [TableName];
287 Columns = new DataColumn [KeyRef.Fields.Count];
289 foreach (XmlSchemaXPath Field in KeyRef.Fields) {
290 if (Table.Columns.Contains (Field.XPath)) {
291 Columns [i] = Table.Columns [Field.XPath];
295 string name = KeyRef.Refer.Name;
296 // get the unique constraint for the releation
297 UniqueConstraint constraint = GetDSConstraint(name, collection);
298 DataRelation relation = new DataRelation(KeyRef.Name, constraint.Columns, Columns);
299 if (KeyRef.UnhandledAttributes != null){
300 foreach (XmlAttribute attr in KeyRef.UnhandledAttributes){
301 if (attr.LocalName == "IsNested"){
302 if (attr.Value == "true")
303 relation.Nested = true;
308 DSet.Relations.Add(relation);
312 // get the unique constraint for the relation.
313 // name - the name of the XmlSchemaUnique element
314 private UniqueConstraint GetDSConstraint(string name, XmlSchemaObjectCollection collection)
316 // find the element in the constraint collection.
317 foreach (XmlSchemaObject shemaObj in collection){
318 if (shemaObj is XmlSchemaUnique){
319 XmlSchemaUnique unique = (XmlSchemaUnique) shemaObj;
320 if (unique.Name == name){
321 string tableName = unique.Selector.XPath;
322 if (tableName.StartsWith (".//"))
323 tableName = tableName.Substring (3);
325 // find the table in the dataset.
326 if (DSet.Tables.Contains(tableName)){
327 DataTable table = DSet.Tables[tableName];
328 string constraintName = unique.Name;
329 // find if there is an attribute with the constraint name
330 // if not use the XmlSchemaUnique name.
331 if (unique.UnhandledAttributes != null){
332 foreach (XmlAttribute attr in unique.UnhandledAttributes){
333 if (attr.LocalName == "ConstraintName"){
334 constraintName = attr.Value;
339 if (table.Constraints.Contains(constraintName))
340 return (UniqueConstraint)table.Constraints[constraintName];
350 #endregion // Private methods
352 #region Private listeners
354 private void OnXmlSchemaValidation (object sender, ValidationEventArgs args)
359 #endregion // Private listeners
361 #region Private TypeReaders
363 // Reads XmlSchemaType
364 private void ReadXmlSchemaType (XmlSchemaType SchemaType)
366 ReadXmlSchemaType (SchemaType, (DataTable)null);
369 // Reads XmlSchemaType and decides is it Complex or Simple and continue reading those types
370 private void ReadXmlSchemaType (XmlSchemaType SchemaType, DataTable Table)
372 if (SchemaType is XmlSchemaComplexType)
373 ReadXmlSchemaComplexType ((XmlSchemaComplexType)SchemaType, Table);
374 else if (SchemaType is XmlSchemaSimpleType)
375 ReadXmlSchemaSimpleType ((XmlSchemaSimpleType)SchemaType, Table);
378 // Same as above but with DataColumn
379 private void ReadXmlSchemaType (XmlSchemaType SchemaType, DataColumn Column)
381 if (SchemaType is XmlSchemaComplexType)
382 ReadXmlSchemaComplexType ((XmlSchemaComplexType)SchemaType, Column);
383 else if (SchemaType is XmlSchemaSimpleType)
384 ReadXmlSchemaSimpleType ((XmlSchemaSimpleType)SchemaType, Column);
387 #endregion // PrivateTypeReader
389 #region TypeReaderHelppers
391 private void ReadXmlSchemaSimpleType (XmlSchemaSimpleType SimpleType, DataColumn Column)
394 if (SimpleType.Content is XmlSchemaSimpleTypeRestriction)
395 ReadXmlSchemaSimpleTypeRestriction ((XmlSchemaSimpleTypeRestriction)SimpleType.Content, Column);
399 private void ReadXmlSchemaSimpleType (XmlSchemaSimpleType SimpleType, DataTable Table)
401 // TODO: Is it possible that Table-element have simpletype???
405 private void ReadXmlSchemaSimpleTypeRestriction (XmlSchemaSimpleTypeRestriction Restriction, DataColumn Column)
407 foreach (XmlSchemaObject Facet in Restriction.Facets) {
409 // FIXME: I dont know are everyone of these needed but, let them be here for now
410 if (Facet is XmlSchemaMaxLengthFacet)
411 Column.MaxLength = Int32.Parse(((XmlSchemaFacet)Facet).Value);
412 //else if (Facet is XmlSchemaMinLengthFacet)
414 //else if (Facet is XmlSchemaLengthFacet)
416 //else if (Facet is XmlSchemaPatternFacet)
418 //else if (Facet is XmlSchemaEnumerationFacet)
420 //else if (Facet is XmlSchemaMaxInclusiveFacet)
422 //else if (Facet is XmlSchemaMaxExclusiveFacet)
424 //else if (Facet is XmlSchemaMinInclusiveFacet)
426 //else if (Facet is XmlSchemaMinExclusiveFacet)
428 //else if (Facet is XmlSchemaFractionDigitsFacet)
430 //else if (Facet is XmlSchemaTotalDigitsFacet)
432 //else if (Facet is XmlSchemaWhiteSpaceFacet)
438 private void ReadXmlSchemaComplexType (XmlSchemaComplexType Type, DataColumn Column)
440 // TODO: is it possible that column-element have complextype
443 // Reads XmlSchemaComplexType with DataTable
444 private void ReadXmlSchemaComplexType (XmlSchemaComplexType Type, DataTable Table)
446 XmlSchemaComplexType ComplexType = Type as XmlSchemaComplexType;
448 if (ComplexType.Name != null && ComplexType.Name != string.Empty) {
450 if (ElementCollection.Contains (ComplexType.Name)) {
452 if (ComplexType.Particle is XmlSchemaChoice) {
453 ReadXmlSchemaChoice (ComplexType.Particle as XmlSchemaChoice);
455 else if (ComplexType.Particle is XmlSchemaSequence) {
457 DataTable TempTable = ElementCollection [ComplexType.Name] as DataTable;
458 ElementCollection.Remove (ComplexType.Name);
459 ReadXmlSchemaSequence (ComplexType.Particle as XmlSchemaSequence, TempTable);
462 else if (ComplexType.Name != null && !TypeCollection.Contains (ComplexType.Name)) {
463 TypeCollection.Add (ComplexType.Name, ComplexType);
467 // If we are here it means that types of elements are Tables :-P
468 if (ComplexType.Particle is XmlSchemaSequence)
469 ReadXmlSchemaSequence (ComplexType.Particle as XmlSchemaSequence, Table);
474 XmlSchemaParticle Particle;
475 if ((Particle = ComplexType.Particle as XmlSchemaChoice) != null) {
476 ReadXmlSchemaChoice (Particle as XmlSchemaChoice);
478 else if ((Particle = ComplexType.Particle as XmlSchemaSequence) != null) {
479 ReadXmlSchemaSequence (Particle as XmlSchemaSequence, Table);
484 #endregion // TypeReaderHelppers