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.Schema;
\r
19 using System.Xml.Serialization;
\r
21 namespace System.Data
\r
23 internal enum ElementMappingType {
\r
29 internal class TableMappingCollection : CollectionBase
\r
31 public void Add (TableMapping map)
\r
33 this.List.Add (map);
\r
36 public TableMapping this [string name] {
\r
38 foreach (TableMapping map in List)
\r
39 if (map.Table.TableName == name)
\r
46 internal class TableMapping
\r
48 private bool existsInDataSet;
\r
50 public DataTable Table;
\r
51 public ArrayList Elements = new ArrayList ();
\r
52 public ArrayList Attributes = new ArrayList ();
\r
53 public DataColumn SimpleContent;
\r
54 public DataColumn PrimaryKey;
\r
55 public DataColumn ReferenceKey;
\r
57 // Parent TableMapping
\r
58 public TableMapping ParentTable;
\r
60 // decoded LocalName -> TableMapping
\r
61 public TableMappingCollection ChildTables = new TableMappingCollection ();
\r
63 public TableMapping (string name, string ns)
\r
65 Table = new DataTable (name);
\r
66 Table.Namespace = ns;
\r
69 public TableMapping (DataTable dt)
\r
71 existsInDataSet = true;
\r
73 foreach (DataColumn col in dt.Columns) {
\r
74 switch (col.ColumnMapping) {
\r
75 case MappingType.Element:
\r
78 case MappingType.Attribute:
\r
79 Attributes.Add (col);
\r
81 case MappingType.SimpleContent:
\r
82 SimpleContent = col;
\r
86 PrimaryKey = dt.PrimaryKey.Length > 0 ? dt.PrimaryKey [0] : null;
\r
89 public bool ExistsInDataSet {
\r
90 get { return existsInDataSet; }
\r
93 public bool ContainsColumn (string name)
\r
95 return GetColumn (name) != null;
\r
98 public DataColumn GetColumn (string name)
100 foreach (DataColumn col in Elements)
101 if (col.ColumnName == name)
103 foreach (DataColumn col in Attributes)
104 if (col.ColumnName == name)
106 if (SimpleContent != null && name == SimpleContent.ColumnName)
107 return SimpleContent;
108 if (PrimaryKey != null && name == PrimaryKey.ColumnName)
113 public void RemoveElementColumn (string name)
115 foreach (DataColumn col in Elements) {
116 if (col.ColumnName == name) {
117 Elements.Remove (col);
124 internal class XmlDataInferenceLoader
\r
126 const string XmlnsNS = "http://www.w3.org/2000/xmlns/";
\r
128 public static void Infer (DataSet dataset, XmlDocument document, XmlReadMode mode, string [] ignoredNamespaces)
\r
130 new XmlDataInferenceLoader (dataset, document, mode, ignoredNamespaces).ReadXml ();
\r
133 private XmlDataInferenceLoader (DataSet ds, XmlDocument doc, XmlReadMode mode, string [] ignoredNamespaces)
\r
138 this.ignoredNamespaces = ignoredNamespaces != null ? new ArrayList (ignoredNamespaces) : new ArrayList ();
\r
140 // Fill existing table info
\r
141 foreach (DataTable dt in dataset.Tables)
\r
142 tables.Add (new TableMapping (dt));
\r
146 XmlDocument document;
\r
148 ArrayList ignoredNamespaces;
\r
149 TableMappingCollection tables = new TableMappingCollection ();
\r
150 RelationStructureCollection relations = new RelationStructureCollection ();
\r
152 private void ReadXml ()
\r
154 if (document.DocumentElement == null)
\r
157 // If the root element is not a data table, treat
\r
158 // this element as DataSet.
\r
159 // Read one element. It might be DataSet element.
\r
160 XmlElement el = document.DocumentElement;
\r
162 if (IsDocumentElementTable ())
\r
163 InferTopLevelTable (el);
\r
165 string localName = XmlConvert.DecodeName (el.LocalName);
\r
166 dataset.DataSetName = localName;
\r
167 dataset.Namespace = el.NamespaceURI;
\r
168 dataset.Prefix = el.Prefix;
\r
169 foreach (XmlNode n in el.ChildNodes) {
\r
170 if (n.NamespaceURI == XmlSchema.Namespace)
\r
172 if (n.NodeType == XmlNodeType.Element)
\r
173 InferTopLevelTable (n as XmlElement);
\r
177 foreach (TableMapping map in tables) {
\r
178 foreach (TableMapping ct in map.ChildTables) {
\r
179 ct.ReferenceKey = GetMappedColumn (ct, map.Table.TableName + "_Id", map.Table.Prefix, map.Table.Namespace, MappingType.Hidden);
\r
183 foreach (TableMapping map in tables) {
\r
184 if (map.ExistsInDataSet)
\r
186 if (map.PrimaryKey != null)
\r
187 map.Table.Columns.Add (map.PrimaryKey);
\r
188 foreach (DataColumn col in map.Elements)
\r
189 map.Table.Columns.Add (col);
\r
190 foreach (DataColumn col in map.Attributes)
\r
191 map.Table.Columns.Add (col);
\r
192 if (map.SimpleContent != null)
\r
193 map.Table.Columns.Add (map.SimpleContent);
\r
194 if (map.ReferenceKey != null)
\r
195 map.Table.Columns.Add (map.ReferenceKey);
\r
196 dataset.Tables.Add (map.Table);
\r
199 foreach (RelationStructure rs in relations) {
\r
200 string relName = rs.ExplicitName != null ? rs.ExplicitName : rs.ParentTableName + "_" + rs.ChildTableName;
\r
201 DataTable pt = dataset.Tables [rs.ParentTableName];
\r
202 DataTable ct = dataset.Tables [rs.ChildTableName];
\r
203 DataColumn pc = pt.Columns [rs.ParentColumnName];
\r
204 DataColumn cc = ct.Columns [rs.ChildColumnName];
\r
206 throw new DataException ("Parent table was not found : " + rs.ParentTableName);
\r
207 else if (ct == null)
\r
208 throw new DataException ("Child table was not found : " + rs.ChildTableName);
\r
209 else if (pc == null)
\r
210 throw new DataException ("Parent column was not found :" + rs.ParentColumnName);
\r
211 else if (cc == null)
\r
212 throw new DataException ("Child column was not found :" + rs.ChildColumnName);
\r
213 DataRelation rel = new DataRelation (relName, pc, cc, rs.CreateConstraint);
\r
216 rel.ParentTable.PrimaryKey = rel.ParentColumns;
\r
218 dataset.Relations.Add (rel);
\r
222 private void InferTopLevelTable (XmlElement el)
\r
224 InferTableElement (null, el);
\r
227 private void InferColumnElement (TableMapping table, XmlElement el)
\r
229 string localName = XmlConvert.DecodeName (el.LocalName);
\r
230 DataColumn col = table.GetColumn (localName);
\r
232 if (col.ColumnMapping != MappingType.Element)
\r
233 throw new DataException (String.Format ("Column {0} is already mapped to {1}.", localName, col.ColumnMapping));
\r
236 if (table.ChildTables [localName] != null)
\r
237 // Child is already mapped, or infered as a table
\r
238 // (in that case, that takes precedence than
\r
239 // this simple column inference.)
\r
242 col = new DataColumn (localName, typeof (string));
\r
243 col.Namespace = el.NamespaceURI;
\r
244 col.Prefix = el.Prefix;
\r
245 table.Elements.Add (col);
\r
248 private void CheckExtraneousElementColumn (TableMapping parentTable, XmlElement el)
\r
250 if (parentTable == null)
\r
252 string localName = XmlConvert.DecodeName (el.LocalName);
\r
253 DataColumn elc = parentTable.GetColumn (localName);
\r
255 parentTable.RemoveElementColumn (localName);
\r
258 private void PopulatePrimaryKey (TableMapping table)
\r
260 if (table.PrimaryKey != null) {
\r
261 if (table.PrimaryKey.ColumnName != table.Table.TableName + "_Id")
\r
262 throw new DataException ("There is already a primary key column.");
\r
265 DataColumn col = new DataColumn (table.Table.TableName + "_Id");
\r
266 col.ColumnMapping = MappingType.Hidden;
\r
267 col.DataType = typeof (int);
\r
268 col.AllowDBNull = false;
\r
269 col.AutoIncrement = true;
\r
270 col.Namespace = table.Table.Namespace;
\r
271 col.Prefix = table.Table.Prefix;
\r
272 table.PrimaryKey = col;
\r
275 private void PopulateRelationStructure (string parent, string child)
\r
277 if (relations [parent, child] != null)
\r
280 RelationStructure rs = new RelationStructure ();
\r
281 rs.ParentTableName = parent;
\r
282 rs.ChildTableName = child;
\r
283 rs.ParentColumnName = parent + "_Id";
\r
284 rs.ChildColumnName = parent + "_Id";
\r
285 rs.CreateConstraint = true;
\r
286 rs.IsNested = true;
\r
287 relations.Add (rs);
\r
290 private void InferRepeatedElement (TableMapping parentTable, XmlElement el)
\r
292 string localName = XmlConvert.DecodeName (el.LocalName);
\r
293 // FIXME: can be checked later
\r
294 CheckExtraneousElementColumn (parentTable, el);
\r
295 TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);
\r
297 // If the mapping is actually complex type (not simple
\r
298 // repeatable), then ignore it.
\r
299 if (table.Elements.Count > 0)
\r
302 // If simple column already exists, do nothing
\r
303 if (table.SimpleContent != null)
\r
306 GetMappedColumn (table, localName + "_Column", el.Prefix, el.NamespaceURI, MappingType.SimpleContent);
\r
309 private void InferTableElement (TableMapping parentTable, XmlElement el)
\r
311 // If parent table already has the same name column but
\r
312 // mapped as Element, that must be removed.
\r
313 // FIXME: This can be done later (doing it here is
\r
314 // loss of performance.
\r
315 CheckExtraneousElementColumn (parentTable, el);
\r
317 string localName = XmlConvert.DecodeName (el.LocalName);
\r
318 TableMapping table = GetMappedTable (parentTable, localName, el.NamespaceURI);
\r
320 bool hasChildElements = false;
\r
321 bool hasAttributes = false;
\r
322 bool hasText = false;
\r
324 foreach (XmlAttribute attr in el.Attributes) {
\r
325 if (attr.NamespaceURI == XmlnsNS)
\r
327 if (ignoredNamespaces.Contains (attr.NamespaceURI))
\r
330 hasAttributes = true;
\r
331 DataColumn col = GetMappedColumn (table,
\r
332 XmlConvert.DecodeName (attr.LocalName),
\r
335 MappingType.Attribute);
\r
338 foreach (XmlNode n in el.ChildNodes) {
\r
339 switch (n.NodeType) {
\r
340 case XmlNodeType.Comment:
\r
341 case XmlNodeType.ProcessingInstruction: // ignore
\r
343 default: // text content
\r
346 case XmlNodeType.Element: // child
\r
347 hasChildElements = true;
\r
348 XmlElement cel = n as XmlElement;
\r
349 string childLocalName = XmlConvert.DecodeName (cel.LocalName);
\r
351 switch (GetElementMappingType (cel)) {
\r
352 case ElementMappingType.Simple:
\r
353 InferColumnElement (table, cel);
\r
355 case ElementMappingType.Repeated:
\r
356 PopulatePrimaryKey (table);
\r
357 PopulateRelationStructure (table.Table.TableName, childLocalName);
\r
358 InferRepeatedElement (table, cel);
\r
360 case ElementMappingType.Complex:
\r
361 PopulatePrimaryKey (table);
\r
362 PopulateRelationStructure (table.Table.TableName, childLocalName);
\r
363 InferTableElement (table, cel);
\r
370 // Attributes + !Children + Text = SimpleContent
\r
371 if (table.SimpleContent == null // no need to create
\r
372 && !hasChildElements && hasText && hasAttributes) {
\r
373 GetMappedColumn (table, table.Table.TableName + "_Text", String.Empty, String.Empty, MappingType.SimpleContent);
\r
377 private TableMapping GetMappedTable (TableMapping parent, string tableName, string ns)
\r
379 TableMapping map = tables [tableName];
\r
381 if (map.ParentTable != parent)
\r
382 throw new DataException (String.Format ("The table {0} is already allocated as another table's child table.", tableName));
\r
384 map = new TableMapping (tableName, ns);
\r
385 map.ParentTable = parent;
\r
387 if (parent != null)
\r
388 parent.ChildTables.Add (map);
\r
393 private DataColumn GetMappedColumn (TableMapping table, string name, string prefix, string ns, MappingType type)
\r
395 DataColumn col = table.GetColumn (name);
\r
398 col = new DataColumn (name);
\r
399 col.Prefix = prefix;
\r
400 col.Namespace = ns;
\r
401 col.ColumnMapping = type;
\r
403 case MappingType.Element:
\r
404 table.Elements.Add (col);
\r
406 case MappingType.Attribute:
\r
407 table.Attributes.Add (col);
\r
409 case MappingType.SimpleContent:
\r
410 table.SimpleContent = col;
\r
412 case MappingType.Hidden:
\r
413 // To generate parent key
\r
414 col.DataType = typeof (int);
\r
415 table.ReferenceKey = col;
\r
419 else if (col.ColumnMapping != type) // Check mapping type
\r
420 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
425 private ElementMappingType GetElementMappingType (XmlElement el)
\r
427 foreach (XmlAttribute attr in el.Attributes) {
\r
428 if (attr.NamespaceURI == XmlnsNS)
\r
430 if (ignoredNamespaces.Contains (attr.NamespaceURI))
\r
432 return ElementMappingType.Complex;
\r
434 foreach (XmlNode n in el.ChildNodes)
\r
435 if (n.NodeType == XmlNodeType.Element)
\r
436 return ElementMappingType.Complex;
\r
438 for (XmlNode n = el.NextSibling; n != null; n = n.NextSibling)
\r
439 if (n.NodeType == XmlNodeType.Element && n.LocalName == el.LocalName)
\r
440 return GetElementMappingType (n as XmlElement) == ElementMappingType.Complex ? ElementMappingType.Complex : ElementMappingType.Repeated;
\r
442 return ElementMappingType.Simple;
\r
445 private bool IsDocumentElementTable ()
\r
447 XmlElement top = document.DocumentElement;
\r
448 foreach (XmlAttribute attr in top.Attributes) {
\r
449 if (attr.NamespaceURI == XmlnsNS)
\r
451 if (ignoredNamespaces.Contains (attr.NamespaceURI))
\r
453 // document element has attributes other than xmlns
\r
456 foreach (XmlNode n in top.ChildNodes) {
\r
457 XmlElement el = n as XmlElement;
\r
460 if (this.GetElementMappingType (el) == ElementMappingType.Simple)
\r
466 // Returns if it "might" be a column element (this method is
\r
467 // called per child element, thus it might still consist of
\r
468 // table, since it might be repeated).
\r
469 private bool IsPossibleColumnElement (XmlElement el)
\r
471 foreach (XmlAttribute attr in el.Attributes) {
\r
472 if (attr.NamespaceURI == "http://www.w3.org/2000/xmlns/")
\r
476 foreach (XmlNode n in el.ChildNodes)
\r
477 if (n.NodeType == XmlNodeType.Element)
\r
486 public class Driver
\r
488 private static void DumpDataTable (DataTable dt)
\r
490 Console.WriteLine ("<Table>");
\r
491 Console.WriteLine (dt.TableName);
\r
492 Console.WriteLine ("ChildRelationCount: " + dt.ChildRelations.Count);
\r
493 Console.WriteLine ("ConstraintsCount: " + dt.Constraints.Count);
\r
494 Console.WriteLine ("ParentRelationCount: " + dt.ParentRelations.Count);
\r
495 Console.WriteLine ("Prefix: " + dt.Prefix);
\r
496 Console.WriteLine ("Namespace: " + dt.Namespace);
\r
497 Console.WriteLine ("Site: " + dt.Site);
\r
498 Console.WriteLine ("RowCount: " + dt.Rows.Count);
\r
499 Console.WriteLine ("<Columns count='" + dt.Columns.Count + "'>");
\r
500 foreach (DataColumn col in dt.Columns)
\r
501 DumpDataColumn (col);
\r
502 Console.WriteLine ("</Columns>");
\r
503 Console.WriteLine ("</Table>");
\r
506 private static void DumpDataRelation (DataRelation rel)
\r
508 Console.WriteLine ("<Relation>");
\r
509 Console.WriteLine (rel.RelationName);
\r
510 Console.WriteLine (rel.Nested);
\r
511 Console.Write (" <ParentColumns>");
\r
512 foreach (DataColumn col in rel.ParentColumns)
\r
513 Console.Write (col.ColumnName + " ");
\r
514 Console.WriteLine ("</ParentColumns>");
\r
515 Console.Write (" <ChildColumns>");
\r
516 foreach (DataColumn col in rel.ChildColumns)
\r
517 Console.Write (col.ColumnName + " ");
\r
518 Console.WriteLine ("</ChildColumns>");
\r
519 if (rel.ParentKeyConstraint != null) {
\r
520 Console.WriteLine (" <ParentKeyConstraint>");
\r
521 DumpUniqueConstraint (rel.ParentKeyConstraint);
\r
522 Console.WriteLine (" </ParentKeyConstraint>");
\r
524 if (rel.ChildKeyConstraint != null) {
\r
525 Console.WriteLine (" <ChildKeyConstraint>");
\r
526 DumpForeignKeyConstraint (rel.ChildKeyConstraint);
\r
527 Console.WriteLine (" </ChildKeyConstraint>");
\r
529 Console.WriteLine ("</Relation>");
\r
532 public static void DumpUniqueConstraint (UniqueConstraint uc)
\r
534 Console.WriteLine ("Name " + uc.ConstraintName);
\r
535 Console.WriteLine ("PK? " + uc.IsPrimaryKey);
\r
536 Console.Write (" <Columns>");
\r
537 foreach (DataColumn col in uc.Columns)
\r
538 Console.Write (col.ColumnName + " ");
\r
539 Console.WriteLine ("</Columns>");
\r
542 public static void DumpForeignKeyConstraint (ForeignKeyConstraint fk)
\r
544 Console.WriteLine ("Name " + fk.ConstraintName);
\r
545 Console.WriteLine (" <Rules>" + fk.AcceptRejectRule + ", " +
\r
546 fk.DeleteRule + ", " + fk.UpdateRule + "</Rules>");
\r
547 Console.Write (" <Columns>");
\r
548 foreach (DataColumn col in fk.Columns)
\r
549 Console.Write (col.ColumnName + " ");
\r
550 Console.WriteLine ("</Columns>");
\r
551 Console.Write (" <RelatedColumns>");
\r
552 foreach (DataColumn col in fk.RelatedColumns)
\r
553 Console.Write (col.ColumnName + " ");
\r
554 Console.WriteLine ("</RelatedColumns>");
\r
557 private static void DumpDataSet (DataSet ds)
\r
559 Console.WriteLine ("-----------------------");
\r
560 Console.WriteLine ("name: " + ds.DataSetName);
\r
561 Console.WriteLine ("ns: " + ds.Namespace);
\r
562 Console.WriteLine ("prefix: " + ds.Prefix);
\r
563 Console.WriteLine ("extprop: " + ds.ExtendedProperties.Count);
\r
564 Console.WriteLine ("<Tables count='" + ds.Tables.Count + "'>");
\r
565 foreach (DataTable dt in ds.Tables)
\r
566 DumpDataTable (dt);
\r
567 Console.WriteLine ("</Tables>");
\r
568 Console.WriteLine ("<Relations count='" + ds.Relations.Count + "'>");
\r
569 foreach (DataRelation rel in ds.Relations)
\r
570 DumpDataRelation (rel);
\r
571 Console.WriteLine ("</Relations>");
\r
574 public static void DumpDataColumn (DataColumn col)
\r
576 Console.WriteLine ("<Column>");
\r
577 Console.WriteLine (" ColumnName: " + col.ColumnName);
\r
578 Console.WriteLine (" AllowDBNull? " + col.AllowDBNull);
\r
579 Console.WriteLine (" AutoIncrement? " + col.AutoIncrement);
\r
580 Console.WriteLine (" Seed: " + col.AutoIncrementSeed);
\r
581 Console.WriteLine (" Step: " + col.AutoIncrementStep);
\r
582 Console.WriteLine (" Caption " + col.Caption);
\r
583 Console.WriteLine (" Mapping: " + col.ColumnMapping);
\r
584 Console.WriteLine (" Type: " + col.DataType);
\r
585 Console.WriteLine (" DefaultValue: " + (col.DefaultValue == DBNull.Value ? "(DBNull)" : col.DefaultValue));
\r
586 Console.WriteLine (" Expression: " + (col.Expression == "" ? "(empty)" : col.Expression));
\r
587 Console.WriteLine (" MaxLength: " + col.MaxLength);
\r
588 Console.WriteLine (" Namespace: " + (col.Namespace == null ? "(null)" : col.Namespace));
\r
589 Console.WriteLine (" Ordinal: " + col.Ordinal);
\r
590 Console.WriteLine (" Prefix: " + (col.Prefix == null ? "(null)" : col.Prefix));
\r
591 Console.WriteLine (" ReadOnly: " + col.ReadOnly);
\r
592 Console.WriteLine (" Unique: " + col.Unique);
\r
593 Console.WriteLine ("</Column>");
\r
596 public static void Main (string [] args)
\r
598 if (args.Length < 1) {
\r
599 Console.WriteLine ("reader.exe xmlfilename");
\r
603 XmlSerializer ser = new XmlSerializer (typeof (DataSet));
\r
605 DataSet ds = new DataSet ();
\r
606 XmlTextReader xtr = new XmlTextReader (args [0]);
\r
607 ds.ReadXml (xtr, XmlReadMode.Auto);
\r
609 TextWriter sw = new StringWriter ();
\r
610 ser.Serialize (sw, ds);
\r
611 using (TextWriter w = new StreamWriter (Path.ChangeExtension (args [0], "ms.txt"), false, Encoding.ASCII)) {
\r
612 w.WriteLine (sw.ToString ());
\r
615 ds = new DataSet ();
\r
616 xtr = new XmlTextReader (args [0]);
\r
617 XmlDocument doc = new XmlDocument ();
\r
619 XmlDataInferenceLoader.Infer (ds, doc, XmlReadMode.Auto, null);
\r
621 sw = new StringWriter ();
\r
623 ser.Serialize (sw, ds);
\r
624 using (TextWriter w = new StreamWriter (Path.ChangeExtension (args [0], "my.txt"), false, Encoding.ASCII)) {
\r
625 w.WriteLine (sw.ToString ());
\r
628 } catch (Exception ex) {
\r
629 Console.WriteLine (ex);
\r
639 // This class is used to implement DataSet's ReadXml() and
\r
640 // InferXmlSchema() methods. That is, 1) to infer dataset schema
\r
641 // structure and 2) to read data rows.
\r
643 // It is instantiated from DataSet, XmlReader and XmlReadMode.
\r
646 // ** General Design
\r
650 // Data rows are not read when XmlReadMode is ReadSchema or InferSchema
\r
651 // (well, actually ReadSchema should not be passed).
\r
653 // Schema inference is done only when XmlReadMode is Auto or InferSchema.
\r
655 // *** Information Set
\r
657 // Only elements, "attributes", and "text" are considered. Here "text"
\r
658 // includes text node and CDATA section, but does not include whitespace
\r
659 // and even significant whitespace (as MS does). Also, here "attributes"
\r
660 // does not include "namespace" nodes.
\r
663 // ** Inference Design
\r
667 // There are four type of mapping in MappingType enumeration:
\r
669 // Element : Mapped to a simple type element.
\r
670 // Attribute : Mapped to an attribute
\r
671 // SimpleContent : Mapped to the text content of complex type element.
\r
672 // (i.e. xs:element/xs:complexType/xs:simpleContent)
\r
673 // Hidden : Used for both key and reference, for auto-generated columns.
\r
675 // *** Mapping strategy
\r
677 // Attributes are always (except for namespace nodes) infered as
\r
678 // DataColumn (of MappingType.Attribute).
\r
680 // When an element has attributes, it always becomes a DataTable.
\r
681 // Otherwise if there is a child element, it becomes DataTable as well.
\r
683 // When there is a text content, 1) if the container element has
\r
684 // attribute(s), the text content becomes a SimpleContent DataColumn
\r
685 // in the container "table" element (yes, it becomes a DataTable).
\r
686 // 2) if the container has no attribute, the element becomes DataColumn.
\r
688 // If there are both text content and child element(s), the text content
\r
689 // is ignored (thus, it always become DataTable).
\r
691 // *** Mapping conflicts
\r
693 // If there has been already a different MappingType of DataColumn,
\r
694 // it is DataException. For example, it is an error if there are an
\r
695 // attribute and an element child those names are the same.
\r
697 // Name remapping will never be done. It introduces complicated rules.
\r
699 // *** Upgrading from DataColumn to DataTable
\r
701 // If there has been the same Element type of mapping (that is, when
\r
702 // the same-name child elements appeared in the element), then the
\r
703 // child elements become a DataTable (so here must be a conversion
\r
704 // from DataColumn/value_DataRow to DataTable/value_DataRow in the new
\r
705 // table and reference_to_new_table in the old DataColumn.
\r
708 // ** Implementation
\r
710 // *** XmlReader based implementation
\r
712 // This class uses XmlReader to avoid having the entire XML document
\r
713 // object. The basic stategy is
\r
715 // 1) handle attributes at startElement
\r
716 // 2) store text content (if it "stores" in data rows) while
\r
718 // 3) dispose of elements at endElement
\r
719 // 4) Empty element without attributes is equal to a column
\r
722 // In XmlSchemaMapper.cs (by Ville Palo) there is an enumeration type
\r
723 // ElementType (undefined, table, column). This concept is nice to reuse.
\r
725 // *** Top level inference
\r
727 // The process starts with ReadElement() for the top-level element.
\r
728 // (considering Fragment mode, it might not be the document element.
\r
729 // However, no inference is done in that mode.)
\r
731 // If the top level element was not a DataTable and there is
\r
732 // no more content, the element is regarded as DataSet with no tables.
\r
734 // *** First child of the DataSet element
\r
736 // There are some special cases.
\r
738 // *** ReadElement()
\r
740 // The main inference process is ReadElement(). This method consumes
\r
741 // (should consume) exactly one element and interpret it as either
\r
742 // DataTable or DataColumn.
\r