private void LoadTable(DataTable table, bool isNested)
{
// <DataSet> /--------------------------- We are here on entrance
// <Table>
// <Column>Value</Column>
// <AnotherColumn>Value</AnotherColumn>
// </Table> /-------------------------- We are here on exit
// <AnotherTable>
// ...
// </AnotherTable>
// ...
// </DataSet>
Debug.Assert(table != null, "Table to be loaded is null on LoadTable() entry");
DataRow row = null; // Data row we're going to add to this table
int entryDepth = _dataReader.Depth; // Store current reader depth so we know when to stop reading
int entryChild = _childRowsStack.Count; // Memorize child stack level on entry
DataColumn c; // Hold column here
DataColumnCollection collection = table.Columns; // Hold column collectio here
object[] foundColumns = new object[collection.Count];
// This is the columns data we found
// This is used to process diffgramms
int rowOrder = -1; // Row to insert data to
string diffId = string.Empty; // Diffgram ID string
string hasChanges = null; // Changes string
bool hasErrors = false; // Set this in case of problem
string textNodeValue; // Value of a text node we might have
// Process attributes first
for (int i = _dataReader.AttributeCount - 1; i >= 0; --i)
{
// Check all attributes one by one
_dataReader.MoveToAttribute(i); // Get this attribute
c = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader)) as DataColumn;
// Try to get column for this attribute
if ((c != null) && (c.ColumnMapping == MappingType.Attribute))
{
// Yep, it is a column mapped as attribute
// Get value from XML and store it in the object array
foundColumns[c.Ordinal] = c.ConvertXmlToObject(_dataReader.Value);
} // Oops. No column for this element
if (_isDiffgram)
{ // Now handle some diffgram attributes
if (_dataReader.NamespaceURI == Keywords.DFFNS)
{
switch (_dataReader.LocalName)
{
case Keywords.DIFFID: // Is it a diffgeam ID ?
diffId = _dataReader.Value; // Store ID
break;
case Keywords.HASCHANGES: // Has chages attribute ?
hasChanges = _dataReader.Value; // Store value
break;
case Keywords.HASERRORS: // Has errors attribute ?
hasErrors = (bool)Convert.ChangeType(_dataReader.Value, typeof(bool), CultureInfo.InvariantCulture);
// Store value
break;
}
}
else if (_dataReader.NamespaceURI == Keywords.MSDNS)
{
if (_dataReader.LocalName == Keywords.ROWORDER)
{
// Is it a row order attribute ?
rowOrder = (int)Convert.ChangeType(_dataReader.Value, typeof(int), CultureInfo.InvariantCulture);
// Store it
}
else if (_dataReader.LocalName.StartsWith("hidden", StringComparison.Ordinal))
{
// Hidden column ?
c = collection[XmlConvert.DecodeName(_dataReader.LocalName.Substring(6))];
// Let's see if we have one.
// We have to decode name before we look it up
// We could not use XmlToDataSet map as it contains
// no hidden columns
if ((c != null) && (c.ColumnMapping == MappingType.Hidden))
{
// Got column and it is hidden ?
foundColumns[c.Ordinal] = c.ConvertXmlToObject(_dataReader.Value);
}
}
}
}
} // Done with attributes
// Now handle elements. This could be columns or nested tables.
// <DataSet> /------------------- We are here after dealing with attributes
// <Table foo="FooValue" bar="BarValue">
// <Column>Value</Column>
// <AnotherColumn>Value</AnotherColumn>
// </Table>
// </DataSet>
if (_dataReader.Read() && entryDepth < _dataReader.Depth)
{
// Read to the next element and see if we're inside
while (entryDepth < _dataReader.Depth)
{ // Get out as soon as we've processed all nested nodes.
switch (_dataReader.NodeType)
{ // Process nodes based on type
case XmlNodeType.Element: // It's an element
object o = _nodeToSchemaMap.GetColumnSchema(table, _dataReader, FIgnoreNamespace(_dataReader));
// Get dataset element for this XML element
c = o as DataColumn; // Perhaps, it's a column?
if (c != null)
{ // Do we have matched column in this table?
// Let's load column data
if (foundColumns[c.Ordinal] == null)
{
// If this column was not found before
LoadColumn(c, foundColumns);
// Get column value
}
else
{
_dataReader.Read(); // Advance to next element.
}
}
else
{
DataTable nestedTable = o as DataTable;
// Perhaps, it's a nested table ?
if (nestedTable != null)
{ // Do we have matched nested table in DataSet ?
LoadTable(nestedTable, true /* isNested */);
// Yes. Load nested table (recursive)
} // Not a table nor column? Check if it's schema.
else if (ProcessXsdSchema())
{ // Check for schema. Skip or load if found.
continue; // Schema has been found. Process the next element
// we're already at (done by schema processing).
}
else
{
// We've got element which is not supposed to he here according to the schema.
// That might be a table which was misplaced. We should've thrown on that,
// but we'll try to load it so we could keep compatibility.
// We won't try to match to columns as we have no idea
// which table this potential column might belong to.
DataTable misplacedTable = _nodeToSchemaMap.GetTableForNode(_dataReader, FIgnoreNamespace(_dataReader));
// Try to get table for node
if (misplacedTable != null)
{ // Got some matching table?
LoadTable(misplacedTable, false /* isNested */);
// While table's XML element is nested,
// the table itself is not. Load it this way.
}
else
{
_dataReader.Read(); // Not a table? Try next element.
}
}
}
break;
case XmlNodeType.EntityReference: // Oops. No support for Entity Reference
throw ExceptionBuilder.FoundEntity();
case XmlNodeType.Text: // It looks like a text.
case XmlNodeType.Whitespace: // This actually could be
case XmlNodeType.CDATA: // if we have XmlText in our table
case XmlNodeType.SignificantWhitespace:
textNodeValue = _dataReader.ReadString();
// Get text node value.
c = table._xmlText; // Get XML Text column from our table
if (c != null && foundColumns[c.Ordinal] == null)
{
// If XmlText Column is set
// and we do not have data already
foundColumns[c.Ordinal] = c.ConvertXmlToObject(textNodeValue);
// Read and store the data
}
break;
default:
_dataReader.Read(); // We don't process that, skip to the next element.
break;
}
}
_dataReader.Read(); // We're done here, proceed to the next element.
}
// It's the time to populate row with loaded data and add it to the table we'we just read to the table
if (_isDiffgram)
{ // In case of diffgram
row = table.NewRow(table.NewUninitializedRecord());
// just create an empty row
row.BeginEdit(); // and allow it's population with data
for (int i = foundColumns.Length - 1; i >= 0; --i)
{
// Check all columns
c = collection[i]; // Get column for this index
c[row._tempRecord] = null != foundColumns[i] ? foundColumns[i] : DBNull.Value;
// Set column to loaded value of to
// DBNull if value is missing.
}
row.EndEdit(); // Done with this row
table.Rows.DiffInsertAt(row, rowOrder); // insert data to specific location
// And do some diff processing
if (hasChanges == null)
{ // No changes ?
row._oldRecord = row._newRecord; // Restore old record
}
if ((hasChanges == Keywords.MODIFIED) || hasErrors)
{
table.RowDiffId[diffId] = row;
}
}
else
{
for (int i = foundColumns.Length - 1; i >= 0; --i)
{
// Check all columns
if (null == foundColumns[i])
{ // Got data for this column ?
c = collection[i]; // No. Get column for this index
if (c.AllowDBNull && c.ColumnMapping != MappingType.Hidden && !c.AutoIncrement)
{
foundColumns[i] = DBNull.Value; // Assign DBNull if possible
// table.Rows.Add() below will deal
// with default values and autoincrement
}
}
}
row = table.Rows.AddWithColumnEvents(foundColumns); // Create, populate and add row
}
// Data is loaded into the row and row is added to the table at this point
while (entryChild < _childRowsStack.Count)
{ // Process child rows we might have
DataRow childRow = (DataRow)_childRowsStack.Pop();
// Get row from the stack
bool unchanged = (childRow.RowState == DataRowState.Unchanged);
// Is data the same as before?
childRow.SetNestedParentRow(row, /*setNonNested*/ false);
// Set parent row
if (unchanged) // Restore record if child row's unchanged
childRow._oldRecord = childRow._newRecord;
}
if (isNested) // Got parent ?
_childRowsStack.Push(row); // Push row to the stack
}