2 // XmlDataInferenceLoader.cs
\r
6 // Atsushi Enomoto <atsushi@ximian.com>
\r
8 // (C)2004 Novell Inc.
\r
10 // Design notes are the bottom of the source.
\r
13 using System.Collections;
\r
15 using System.IO; // for Driver
\r
16 using System.Text; // for Driver
\r
18 using System.Xml.Serialization;
\r
20 namespace System.Data
\r
22 internal enum ElementMappingType {
\r
28 internal class TableMappingCollection : CollectionBase
\r
30 public void Add (TableMapping map)
\r
32 this.List.Add (map);
\r
35 public TableMapping this [string name] {
\r
37 foreach (TableMapping map in List)
\r
38 if (map.Table.TableName == name)
\r
45 internal class TableMapping
\r
47 private bool existsInDataSet;
\r
49 public DataTable Table;
\r
50 public ArrayList Elements = new ArrayList ();
\r
51 public ArrayList Attributes = new ArrayList ();
\r
52 public DataColumn SimpleContent;
\r
53 public DataColumn PrimaryKey;
\r
54 public DataColumn ReferenceKey;
\r
56 // Parent TableMapping
\r
57 public TableMapping ParentTable;
\r
59 // decoded LocalName -> TableMapping
\r
60 public TableMappingCollection ChildTables = new TableMappingCollection ();
\r
62 public TableMapping (string name, string ns)
\r
64 Table = new DataTable (name);
\r
65 Table.Namespace = ns;
\r
68 public TableMapping (DataTable dt)
\r
70 existsInDataSet = true;
\r
72 foreach (DataColumn col in dt.Columns) {
\r
73 switch (col.ColumnMapping) {
\r
74 case MappingType.Element:
\r
77 case MappingType.Attribute:
\r
78 Attributes.Add (col);
\r
80 case MappingType.SimpleContent:
\r
81 SimpleContent = col;
\r
85 PrimaryKey = dt.PrimaryKey.Length > 0 ? dt.PrimaryKey [0] : null;
\r
88 public bool ExistsInDataSet {
\r
89 get { return existsInDataSet; }
\r
92 public bool ContainsColumn (string name)
\r
94 return GetColumn (name) != null;
\r
97 public DataColumn GetColumn (string name)
99 foreach (DataColumn col in Elements)
100 if (col.ColumnName == name)
102 foreach (DataColumn col in Attributes)
103 if (col.ColumnName == name)
105 if (SimpleContent != null && name == SimpleContent.ColumnName)
106 return SimpleContent;
107 if (PrimaryKey != null && name == PrimaryKey.ColumnName)
112 public void RemoveElementColumn (string name)
114 foreach (DataColumn col in Elements) {
115 if (col.ColumnName == name) {
116 Elements.Remove (col);
123 internal class XmlDataInferenceLoader
\r
125 const string XmlnsNS = "http://www.w3.org/2000/xmlns/";
\r
127 public static void Infer (DataSet dataset, XmlDocument document, XmlReadMode mode, string [] ignoredNamespaces)
\r
129 new XmlDataInferenceLoader (dataset, document, mode, ignoredNamespaces).ReadXml ();
\r
132 private XmlDataInferenceLoader (DataSet ds, XmlDocument doc, XmlReadMode mode, string [] ignoredNamespaces)
\r
137 this.ignoredNamespaces = ignoredNamespaces != null ? new ArrayList (ignoredNamespaces) : new ArrayList ();
\r
139 // Fill existing table info
\r
140 foreach (DataTable dt in dataset.Tables)
\r
141 tables.Add (new TableMapping (dt));
\r
145 XmlDocument document;
\r
147 ArrayList ignoredNamespaces;
\r
148 TableMappingCollection tables = new TableMappingCollection ();
\r
149 RelationStructureCollection relations = new RelationStructureCollection ();
\r
151 private void ReadXml ()
\r
153 if (document.DocumentElement == null)
\r
156 // If the root element is not a data table, treat
\r
157 // this element as DataSet.
\r
158 // Read one element. It might be DataSet element.
\r
159 XmlElement el = document.DocumentElement;
\r
161 if (IsDocumentElementTable ())
\r
162 InferTopLevelTable (el);
\r
164 string localName = XmlConvert.DecodeName (el.LocalName);
\r
165 dataset.DataSetName = localName;
\r
166 dataset.Namespace = el.NamespaceURI;
\r
167 dataset.Prefix = el.Prefix;
\r
168 foreach (XmlNode n in el.ChildNodes)
\r
169 if (n.NodeType == XmlNodeType.Element)
\r
170 InferTopLevelTable (n as XmlElement);
\r
173 foreach (TableMapping map in tables) {
\r
174 foreach (TableMapping ct in map.ChildTables) {
\r
175 ct.ReferenceKey = GetMappedColumn (ct, map.Table.TableName + "_Id", map.Table.Prefix, map.Table.Namespace, MappingType.Hidden);
\r
179 foreach (TableMapping map in tables) {
\r
180 if (map.ExistsInDataSet)
\r
182 if (map.PrimaryKey != null)
\r
183 map.Table.Columns.Add (map.PrimaryKey);
\r
184 foreach (DataColumn col in map.Elements)
\r
185 map.Table.Columns.Add (col);
\r
186 foreach (DataColumn col in map.Attributes)
\r
187 map.Table.Columns.Add (col);
\r
188 if (map.SimpleContent != null)
\r
189 map.Table.Columns.Add (map.SimpleContent);
\r
190 if (map.ReferenceKey != null)
\r
191 map.Table.Columns.Add (map.ReferenceKey);
\r
192 dataset.Tables.Add (map.Table);
\r
195 foreach (RelationStructure rs in relations) {
\r
196 string relName = rs.ExplicitName != null ? rs.ExplicitName : rs.ParentTableName + "_" + rs.ChildTableName;
\r
197 DataTable pt = dataset.Tables [rs.ParentTableName];
\r
198 DataTable ct = dataset.Tables [rs.ChildTableName];
\r
199 DataColumn pc = pt.Columns [rs.ParentColumnName];
\r
200 DataColumn cc = ct.Columns [rs.ChildColumnName];
\r
202 throw new DataException ("Parent table was not found : " + rs.ParentTableName);
\r
203 else if (ct == null)
\r
204 throw new DataException ("Child table was not found : " + rs.ChildTableName);
\r
205 else if (pc == null)
\r
206 throw new DataException ("Parent column was not found :" + rs.ParentColumnName);
\r
207 else if (cc == null)
\r
208 throw new DataException ("Child column was not found :" + rs.ChildColumnName);
\r
209 DataRelation rel = new DataRelation (relName, pc, cc, rs.CreateConstraint);
\r
212 rel.ParentTable.PrimaryKey = rel.ParentColumns;
\r
214 dataset.Relations.Add (rel);
\r
218 private void InferTopLevelTable (XmlElement el)
\r
220 InferTableElement (null, el);
\r
223 private void InferColumnElement (TableMapping table, XmlElement el)
\r
225 string localName = XmlConvert.DecodeName (el.LocalName);
\r
226 DataColumn col = table.GetColumn (localName);
\r
228 if (col.ColumnMapping != MappingType.Element)
\r
229 throw new DataException (String.Format ("Column {0} is already mapped to {1}.", localName, col.ColumnMapping));
\r
232 if (table.ChildTables [localName] != null)
\r
233 // Child is already mapped, or infered as a table
\r
234 // (in that case, that takes precedence than
\r
235 // this simple column inference.)
\r
238 col = new DataColumn (localName, typeof (string));
\r
239 col.Namespace = el.NamespaceURI;
\r
240 col.Prefix = el.Prefix;
\r
241 table.Elements.Add (col);
\r
244 private void CheckExtraneousElementColumn (TableMapping parentTable, XmlElement el)
\r
246 if (parentTable == null)
\r
248 string localName = XmlConvert.DecodeName (el.LocalName);
\r
249 DataColumn elc = parentTable.GetColumn (localName);
\r
251 parentTable.RemoveElementColumn (localName);
\r
254 private void PopulatePrimaryKey (TableMapping table)
\r
256 if (table.PrimaryKey != null) {
\r
257 if (table.PrimaryKey.ColumnName != table.Table.TableName + "_Id")
\r
258 throw new DataException ("There is already a primary key column.");
\r
261 DataColumn col = new DataColumn (table.Table.TableName + "_Id");
\r
262 col.ColumnMapping = MappingType.Hidden;
\r
263 col.DataType = typeof (int);
\r
264 col.AllowDBNull = false;
\r
265 col.AutoIncrement = true;
\r
266 col.Namespace = table.Table.Namespace;
\r
267 col.Prefix = table.Table.Prefix;
\r
268 table.PrimaryKey = col;
\r
271 private void PopulateRelationStructure (string parent, string child)
\r
273 if (relations [parent, child] != null)
\r
276 RelationStructure rs = new RelationStructure ();
\r
277 rs.ParentTableName = parent;
\r
278 rs.ChildTableName = child;
\r
279 rs.ParentColumnName = parent + "_Id";
\r
280 rs.ChildColumnName = parent + "_Id";
\r
281 rs.CreateConstraint = true;
\r
282 rs.IsNested = true;
\r
283 relations.Add (rs);
\r
286 private void InferRepeatedElement (TableMapping parentTable, XmlElement el)
\r
288 string localName = XmlConvert.DecodeName (el.LocalName);
\r
289 // FIXME: can be checked later
\r
290 CheckExtraneousElementColumn (parentTable, el);
\r
291 TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);
\r
293 // If the mapping is actually complex type (not simple
\r
294 // repeatable), then ignore it.
\r
295 if (table.Elements.Count > 0)
\r
298 // If simple column already exists, do nothing
\r
299 if (table.SimpleContent != null)
\r
302 GetMappedColumn (table, localName + "_Column", el.Prefix, el.NamespaceURI, MappingType.SimpleContent);
\r
305 private void InferTableElement (TableMapping parentTable, XmlElement el)
\r
307 // If parent table already has the same name column but
\r
308 // mapped as Element, that must be removed.
\r
309 // FIXME: This can be done later (doing it here is
\r
310 // loss of performance.
\r
311 CheckExtraneousElementColumn (parentTable, el);
\r
313 string localName = XmlConvert.DecodeName (el.LocalName);
\r
314 TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);
\r
316 bool hasChildElements = false;
\r
317 bool hasAttributes = false;
\r
318 bool hasText = false;
\r
320 foreach (XmlAttribute attr in el.Attributes) {
\r
321 if (attr.NamespaceURI == XmlnsNS)
\r
323 if (ignoredNamespaces.Contains (attr.NamespaceURI))
\r
326 hasAttributes = true;
\r
327 DataColumn col = GetMappedColumn (table,
\r
328 XmlConvert.DecodeName (attr.LocalName),
\r
331 MappingType.Attribute);
\r
334 foreach (XmlNode n in el.ChildNodes) {
\r
335 switch (n.NodeType) {
\r
336 case XmlNodeType.Comment:
\r
337 case XmlNodeType.ProcessingInstruction: // ignore
\r
339 default: // text content
\r
342 case XmlNodeType.Element: // child
\r
343 hasChildElements = true;
\r
344 XmlElement cel = n as XmlElement;
\r
345 string childLocalName = XmlConvert.DecodeName (cel.LocalName);
\r
347 switch (GetElementMappingType (cel)) {
\r
348 case ElementMappingType.Simple:
\r
349 InferColumnElement (table, cel);
\r
351 case ElementMappingType.Repeated:
\r
352 PopulatePrimaryKey (table);
\r
353 PopulateRelationStructure (table.Table.TableName, childLocalName);
\r
354 InferRepeatedElement (table, cel);
\r
356 case ElementMappingType.Complex:
\r
357 PopulatePrimaryKey (table);
\r
358 PopulateRelationStructure (table.Table.TableName, childLocalName);
\r
359 InferTableElement (table, cel);
\r
366 // Attributes + !Children + Text = SimpleContent
\r
367 if (table.SimpleContent == null // no need to create
\r
368 && !hasChildElements && hasText && hasAttributes) {
\r
369 GetMappedColumn (table, table.Table.TableName + "_Text", String.Empty, String.Empty, MappingType.SimpleContent);
\r
373 private TableMapping GetMappedTable (TableMapping parent, string tableName, string ns)
\r
375 TableMapping map = tables [tableName];
\r
377 if (map.ParentTable != parent)
\r
378 throw new DataException (String.Format ("The table {0} is already allocated as another table's child table.", tableName));
\r
380 map = new TableMapping (tableName, ns);
\r
381 map.ParentTable = parent;
\r
383 if (parent != null)
\r
384 parent.ChildTables.Add (map);
\r
389 private DataColumn GetMappedColumn (TableMapping table, string name, string prefix, string ns, MappingType type)
\r
391 DataColumn col = table.GetColumn (name);
\r
394 col = new DataColumn (name);
\r
395 col.Prefix = prefix;
\r
396 col.Namespace = ns;
\r
397 col.ColumnMapping = type;
\r
399 case MappingType.Element:
\r
400 table.Elements.Add (col);
\r
402 case MappingType.Attribute:
\r
403 table.Attributes.Add (col);
\r
405 case MappingType.SimpleContent:
\r
406 table.SimpleContent = col;
\r
408 case MappingType.Hidden:
\r
409 // To generate parent key
\r
410 col.DataType = typeof (int);
\r
411 table.ReferenceKey = col;
\r
415 else if (col.ColumnMapping != type) // Check mapping type
\r
416 throw new DataException (String.Format ("There are already another column that has different mapping type. Column is {0}, existing mapping type is {1}", col.ColumnName, col.ColumnMapping));
\r
421 private ElementMappingType GetElementMappingType (XmlElement el)
\r
423 foreach (XmlAttribute attr in el.Attributes) {
\r
424 if (attr.NamespaceURI == XmlnsNS)
\r
426 if (ignoredNamespaces.Contains (attr.NamespaceURI))
\r
428 return ElementMappingType.Complex;
\r
430 foreach (XmlNode n in el.ChildNodes)
\r
431 if (n.NodeType == XmlNodeType.Element)
\r
432 return ElementMappingType.Complex;
\r
434 for (XmlNode n = el.NextSibling; n != null; n = n.NextSibling)
\r
435 if (n.NodeType == XmlNodeType.Element && n.LocalName == el.LocalName)
\r
436 return GetElementMappingType (n as XmlElement) == ElementMappingType.Complex ? ElementMappingType.Complex : ElementMappingType.Repeated;
\r
438 return ElementMappingType.Simple;
\r
441 private bool IsDocumentElementTable ()
\r
443 XmlElement top = document.DocumentElement;
\r
444 foreach (XmlAttribute attr in top.Attributes) {
\r
445 if (attr.NamespaceURI == XmlnsNS)
\r
447 if (ignoredNamespaces.Contains (attr.NamespaceURI))
\r
449 // document element has attributes other than xmlns
\r
452 foreach (XmlNode n in top.ChildNodes) {
\r
453 XmlElement el = n as XmlElement;
\r
456 if (this.GetElementMappingType (el) == ElementMappingType.Simple)
\r
462 // Returns if it "might" be a column element (this method is
\r
463 // called per child element, thus it might still consist of
\r
464 // table, since it might be repeated).
\r
465 private bool IsPossibleColumnElement (XmlElement el)
\r
467 foreach (XmlAttribute attr in el.Attributes) {
\r
468 if (attr.NamespaceURI == "http://www.w3.org/2000/xmlns/")
\r
472 foreach (XmlNode n in el.ChildNodes)
\r
473 if (n.NodeType == XmlNodeType.Element)
\r
482 public class Driver
\r
484 private static void DumpDataTable (DataTable dt)
\r
486 Console.WriteLine ("<Table>");
\r
487 Console.WriteLine (dt.TableName);
\r
488 Console.WriteLine ("ChildRelationCount: " + dt.ChildRelations.Count);
\r
489 Console.WriteLine ("ConstraintsCount: " + dt.Constraints.Count);
\r
490 Console.WriteLine ("ParentRelationCount: " + dt.ParentRelations.Count);
\r
491 Console.WriteLine ("Prefix: " + dt.Prefix);
\r
492 Console.WriteLine ("Namespace: " + dt.Namespace);
\r
493 Console.WriteLine ("Site: " + dt.Site);
\r
494 Console.WriteLine ("RowCount: " + dt.Rows.Count);
\r
495 Console.WriteLine ("<Columns count='" + dt.Columns.Count + "'>");
\r
496 foreach (DataColumn col in dt.Columns)
\r
497 DumpDataColumn (col);
\r
498 Console.WriteLine ("</Columns>");
\r
499 Console.WriteLine ("</Table>");
\r
502 private static void DumpDataRelation (DataRelation rel)
\r
504 Console.WriteLine ("<Relation>");
\r
505 Console.WriteLine (rel.RelationName);
\r
506 Console.WriteLine (rel.Nested);
\r
507 Console.Write (" <ParentColumns>");
\r
508 foreach (DataColumn col in rel.ParentColumns)
\r
509 Console.Write (col.ColumnName + " ");
\r
510 Console.WriteLine ("</ParentColumns>");
\r
511 Console.Write (" <ChildColumns>");
\r
512 foreach (DataColumn col in rel.ChildColumns)
\r
513 Console.Write (col.ColumnName + " ");
\r
514 Console.WriteLine ("</ChildColumns>");
\r
515 if (rel.ParentKeyConstraint != null) {
\r
516 Console.WriteLine (" <ParentKeyConstraint>");
\r
517 DumpUniqueConstraint (rel.ParentKeyConstraint);
\r
518 Console.WriteLine (" </ParentKeyConstraint>");
\r
520 if (rel.ChildKeyConstraint != null) {
\r
521 Console.WriteLine (" <ChildKeyConstraint>");
\r
522 DumpForeignKeyConstraint (rel.ChildKeyConstraint);
\r
523 Console.WriteLine (" </ChildKeyConstraint>");
\r
525 Console.WriteLine ("</Relation>");
\r
528 public static void DumpUniqueConstraint (UniqueConstraint uc)
\r
530 Console.WriteLine ("Name " + uc.ConstraintName);
\r
531 Console.WriteLine ("PK? " + uc.IsPrimaryKey);
\r
532 Console.Write (" <Columns>");
\r
533 foreach (DataColumn col in uc.Columns)
\r
534 Console.Write (col.ColumnName + " ");
\r
535 Console.WriteLine ("</Columns>");
\r
538 public static void DumpForeignKeyConstraint (ForeignKeyConstraint fk)
\r
540 Console.WriteLine ("Name " + fk.ConstraintName);
\r
541 Console.WriteLine (" <Rules>" + fk.AcceptRejectRule + ", " +
\r
542 fk.DeleteRule + ", " + fk.UpdateRule + "</Rules>");
\r
543 Console.Write (" <Columns>");
\r
544 foreach (DataColumn col in fk.Columns)
\r
545 Console.Write (col.ColumnName + " ");
\r
546 Console.WriteLine ("</Columns>");
\r
547 Console.Write (" <RelatedColumns>");
\r
548 foreach (DataColumn col in fk.RelatedColumns)
\r
549 Console.Write (col.ColumnName + " ");
\r
550 Console.WriteLine ("</RelatedColumns>");
\r
553 private static void DumpDataSet (DataSet ds)
\r
555 Console.WriteLine ("-----------------------");
\r
556 Console.WriteLine ("name: " + ds.DataSetName);
\r
557 Console.WriteLine ("ns: " + ds.Namespace);
\r
558 Console.WriteLine ("prefix: " + ds.Prefix);
\r
559 Console.WriteLine ("extprop: " + ds.ExtendedProperties.Count);
\r
560 Console.WriteLine ("<Tables count='" + ds.Tables.Count + "'>");
\r
561 foreach (DataTable dt in ds.Tables)
\r
562 DumpDataTable (dt);
\r
563 Console.WriteLine ("</Tables>");
\r
564 Console.WriteLine ("<Relations count='" + ds.Relations.Count + "'>");
\r
565 foreach (DataRelation rel in ds.Relations)
\r
566 DumpDataRelation (rel);
\r
567 Console.WriteLine ("</Relations>");
\r
570 public static void DumpDataColumn (DataColumn col)
\r
572 Console.WriteLine ("<Column>");
\r
573 Console.WriteLine (" ColumnName: " + col.ColumnName);
\r
574 Console.WriteLine (" AllowDBNull? " + col.AllowDBNull);
\r
575 Console.WriteLine (" AutoIncrement? " + col.AutoIncrement);
\r
576 Console.WriteLine (" Seed: " + col.AutoIncrementSeed);
\r
577 Console.WriteLine (" Step: " + col.AutoIncrementStep);
\r
578 Console.WriteLine (" Caption " + col.Caption);
\r
579 Console.WriteLine (" Mapping: " + col.ColumnMapping);
\r
580 Console.WriteLine (" Type: " + col.DataType);
\r
581 Console.WriteLine (" DefaultValue: " + (col.DefaultValue == DBNull.Value ? "(DBNull)" : col.DefaultValue));
\r
582 Console.WriteLine (" Expression: " + (col.Expression == "" ? "(empty)" : col.Expression));
\r
583 Console.WriteLine (" MaxLength: " + col.MaxLength);
\r
584 Console.WriteLine (" Namespace: " + (col.Namespace == null ? "(null)" : col.Namespace));
\r
585 Console.WriteLine (" Ordinal: " + col.Ordinal);
\r
586 Console.WriteLine (" Prefix: " + (col.Prefix == null ? "(null)" : col.Prefix));
\r
587 Console.WriteLine (" ReadOnly: " + col.ReadOnly);
\r
588 Console.WriteLine (" Unique: " + col.Unique);
\r
589 Console.WriteLine ("</Column>");
\r
592 public static void Main (string [] args)
\r
594 if (args.Length < 1) {
\r
595 Console.WriteLine ("reader.exe xmlfilename");
\r
599 XmlSerializer ser = new XmlSerializer (typeof (DataSet));
\r
601 DataSet ds = new DataSet ();
\r
602 XmlTextReader xtr = new XmlTextReader (args [0]);
\r
603 ds.ReadXml (xtr, XmlReadMode.Auto);
\r
605 TextWriter sw = new StringWriter ();
\r
606 ser.Serialize (sw, ds);
\r
607 using (TextWriter w = new StreamWriter (Path.ChangeExtension (args [0], "ms.txt"), false, Encoding.ASCII)) {
\r
608 w.WriteLine (sw.ToString ());
\r
611 ds = new DataSet ();
\r
612 xtr = new XmlTextReader (args [0]);
\r
613 XmlDocument doc = new XmlDocument ();
\r
615 XmlDataInferenceLoader.Infer (ds, doc, XmlReadMode.Auto, null);
\r
617 sw = new StringWriter ();
\r
619 ser.Serialize (sw, ds);
\r
620 using (TextWriter w = new StreamWriter (Path.ChangeExtension (args [0], "my.txt"), false, Encoding.ASCII)) {
\r
621 w.WriteLine (sw.ToString ());
\r
624 } catch (Exception ex) {
\r
625 Console.WriteLine (ex);
\r
635 // This class is used to implement DataSet's ReadXml() and
\r
636 // InferXmlSchema() methods. That is, 1) to infer dataset schema
\r
637 // structure and 2) to read data rows.
\r
639 // It is instantiated from DataSet, XmlReader and XmlReadMode.
\r
642 // ** General Design
\r
646 // Data rows are not read when XmlReadMode is ReadSchema or InferSchema
\r
647 // (well, actually ReadSchema should not be passed).
\r
649 // Schema inference is done only when XmlReadMode is Auto or InferSchema.
\r
651 // *** Information Set
\r
653 // Only elements, "attributes", and "text" are considered. Here "text"
\r
654 // includes text node and CDATA section, but does not include whitespace
\r
655 // and even significant whitespace (as MS does). Also, here "attributes"
\r
656 // does not include "namespace" nodes.
\r
659 // ** Inference Design
\r
663 // There are four type of mapping in MappingType enumeration:
\r
665 // Element : Mapped to a simple type element.
\r
666 // Attribute : Mapped to an attribute
\r
667 // SimpleContent : Mapped to the text content of complex type element.
\r
668 // (i.e. xs:element/xs:complexType/xs:simpleContent)
\r
669 // Hidden : Used for both key and reference, for auto-generated columns.
\r
671 // *** Mapping strategy
\r
673 // Attributes are always (except for namespace nodes) infered as
\r
674 // DataColumn (of MappingType.Attribute).
\r
676 // When an element has attributes, it always becomes a DataTable.
\r
677 // Otherwise if there is a child element, it becomes DataTable as well.
\r
679 // When there is a text content, 1) if the container element has
\r
680 // attribute(s), the text content becomes a SimpleContent DataColumn
\r
681 // in the container "table" element (yes, it becomes a DataTable).
\r
682 // 2) if the container has no attribute, the element becomes DataColumn.
\r
684 // If there are both text content and child element(s), the text content
\r
685 // is ignored (thus, it always become DataTable).
\r
687 // *** Mapping conflicts
\r
689 // If there has been already a different MappingType of DataColumn,
\r
690 // it is DataException. For example, it is an error if there are an
\r
691 // attribute and an element child those names are the same.
\r
693 // Name remapping will never be done. It introduces complicated rules.
\r
695 // *** Upgrading from DataColumn to DataTable
\r
697 // If there has been the same Element type of mapping (that is, when
\r
698 // the same-name child elements appeared in the element), then the
\r
699 // child elements become a DataTable (so here must be a conversion
\r
700 // from DataColumn/value_DataRow to DataTable/value_DataRow in the new
\r
701 // table and reference_to_new_table in the old DataColumn.
\r
704 // ** Implementation
\r
706 // *** XmlReader based implementation
\r
708 // This class uses XmlReader to avoid having the entire XML document
\r
709 // object. The basic stategy is
\r
711 // 1) handle attributes at startElement
\r
712 // 2) store text content (if it "stores" in data rows) while
\r
714 // 3) dispose of elements at endElement
\r
715 // 4) Empty element without attributes is equal to a column
\r
718 // In XmlSchemaMapper.cs (by Ville Palo) there is an enumeration type
\r
719 // ElementType (undefined, table, column). This concept is nice to reuse.
\r
721 // *** Top level inference
\r
723 // The process starts with ReadElement() for the top-level element.
\r
724 // (considering Fragment mode, it might not be the document element.
\r
725 // However, no inference is done in that mode.)
\r
727 // If the top level element was not a DataTable and there is
\r
728 // no more content, the element is regarded as DataSet with no tables.
\r
730 // *** First child of the DataSet element
\r
732 // There are some special cases.
\r
734 // *** ReadElement()
\r
736 // The main inference process is ReadElement(). This method consumes
\r
737 // (should consume) exactly one element and interpret it as either
\r
738 // DataTable or DataColumn.
\r