// System.Data.DataTableReader.cs
//
// Author:
-// Tim Coleman (tim@timcoleman.com)
+// Sureshkumar T <tsureshkumar@novell.com>
+// Tim Coleman (tim@timcoleman.com)
//
// Copyright (C) Tim Coleman, 2003
//
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
-//
+//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
using System.Collections;
using System.Data.Common;
+using System.ComponentModel;
+using System.Text.RegularExpressions;
namespace System.Data {
- public sealed class DataTableReader : DbDataReader
- {
- bool closed;
- DataTable[] dataTables;
- int index;
+ public sealed class DataTableReader : DbDataReader {
+ bool _closed;
+ DataTable [] _tables;
+ int _current = -1;
+ int _index;
+ DataTable _schemaTable;
+ bool _tableCleared = false;
+ bool _subscribed = false;
+ DataRow _rowRef;
+ bool _schemaChanged = false;
#region Constructors
- [MonoTODO]
public DataTableReader (DataTable dt)
: this (new DataTable[] {dt})
{
}
- [MonoTODO]
public DataTableReader (DataTable[] dataTables)
{
- this.dataTables = dataTables;
- closed = false;
- index = 0;
+ if (dataTables == null || dataTables.Length <= 0)
+ throw new ArgumentException ("Cannot Create DataTable. Argument Empty!");
+
+ this._tables = new DataTable [dataTables.Length];
+
+ for (int i = 0; i < dataTables.Length; i++)
+ this._tables [i] = dataTables [i];
+
+ _closed = false;
+ _index = 0;
+ _current = -1;
+ _rowRef = null;
+ _tableCleared = false;
+
+ SubscribeEvents ();
}
#endregion // Constructors
}
public override int FieldCount {
- get { return dataTables [index].Columns.Count; }
+ get { return CurrentTable.Columns.Count; }
}
public override bool HasRows {
- get { return dataTables [index].Rows.Count > 0; }
+ get { return CurrentTable.Rows.Count > 0; }
}
public override bool IsClosed {
- get { return closed; }
+ get { return _closed; }
}
- [MonoTODO]
public override object this [int index] {
- get { throw new NotImplementedException (); }
+ get {
+ Validate ();
+ if (index < 0 || index >= FieldCount)
+ throw new ArgumentOutOfRangeException ("index " + index + " is not in the range");
+ DataRow row = CurrentRow;
+ if (row.RowState == DataRowState.Deleted)
+ throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
+ return row [index];
+ }
}
- [MonoTODO]
- public override object this [string name] {
- get { throw new NotImplementedException (); }
+ private DataTable CurrentTable {
+ get { return _tables [_index]; }
}
- [MonoTODO]
- public override int RecordsAffected {
- get { throw new NotImplementedException (); }
+ private DataRow CurrentRow {
+ get { return (DataRow) CurrentTable.Rows [_current]; }
}
- [MonoTODO]
- public override int VisibleFieldCount {
- get { throw new NotImplementedException (); }
+
+ public override object this [string name] {
+ get {
+ Validate ();
+ DataRow row = CurrentRow;
+ if (row.RowState == DataRowState.Deleted)
+ throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
+ return row [name];
+ }
+ }
+
+ public override int RecordsAffected {
+ get { return 0; }
}
#endregion // Properties
#region Methods
- [MonoTODO]
- public override void Close ()
+ private void SubscribeEvents ()
+ {
+ if (_subscribed) // avoid subscribing multiple times
+ return;
+ CurrentTable.TableCleared += new DataTableClearEventHandler (OnTableCleared);
+ CurrentTable.RowChanged += new DataRowChangeEventHandler (OnRowChanged);
+ CurrentTable.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnCollectionChanged);
+ for (int i=0; i < CurrentTable.Columns.Count; ++i)
+ CurrentTable.Columns [i].PropertyChanged += new PropertyChangedEventHandler (OnColumnChanged);
+ _subscribed = true;
+ _schemaChanged = false;
+ }
+
+ private void UnsubscribeEvents ()
{
- closed = true;
+ if (!_subscribed) // avoid un-subscribing multiple times
+ return;
+ CurrentTable.TableCleared -= new DataTableClearEventHandler (OnTableCleared);
+ CurrentTable.RowChanged -= new DataRowChangeEventHandler (OnRowChanged);
+ CurrentTable.Columns.CollectionChanged -= new CollectionChangeEventHandler (OnColumnCollectionChanged);
+ for (int i=0; i < CurrentTable.Columns.Count; ++i)
+ CurrentTable.Columns [i].PropertyChanged -= new PropertyChangedEventHandler (OnColumnChanged);
+ _subscribed = false;
+ _schemaChanged = false;
}
- [MonoTODO]
- public override void Dispose ()
+ public override void Close ()
{
- throw new NotImplementedException ();
+ if (IsClosed)
+ return;
+
+ UnsubscribeEvents ();
+ _closed = true;
}
- [MonoTODO]
public override bool GetBoolean (int i)
{
- throw new NotImplementedException ();
+ return (bool) GetValue (i);
}
- [MonoTODO]
public override byte GetByte (int i)
{
- throw new NotImplementedException ();
+ return (byte) GetValue (i);
}
- [MonoTODO]
public override long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
{
- throw new NotImplementedException ();
+ byte[] value = this [i] as byte[];
+ if (value == null)
+ ThrowInvalidCastException (this [i].GetType (), typeof (byte[]));
+ if (buffer == null)
+ return value.Length;
+ int copylen = length > value.Length ? value.Length : length;
+ Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
+ return copylen;
}
- [MonoTODO]
public override char GetChar (int i)
{
- throw new NotImplementedException ();
+ return (char) GetValue (i);
}
- [MonoTODO]
public override long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
{
- throw new NotImplementedException ();
+ char[] value = this [i] as char[];
+ if (value == null)
+ ThrowInvalidCastException (this [i].GetType (), typeof (char[]));
+ if (buffer == null)
+ return value.Length;
+ int copylen = length > value.Length ? value.Length : length;
+ Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
+ return copylen;
}
- [MonoTODO]
public override string GetDataTypeName (int i)
{
- throw new NotImplementedException ();
+ return GetFieldType (i).ToString ();
}
- [MonoTODO]
public override DateTime GetDateTime (int i)
{
- throw new NotImplementedException ();
+ return (DateTime) GetValue (i);
}
- [MonoTODO]
public override decimal GetDecimal (int i)
{
- throw new NotImplementedException ();
+ return (decimal) GetValue (i);
}
- [MonoTODO]
public override double GetDouble (int i)
{
- throw new NotImplementedException ();
+ return (double) GetValue (i);
}
- [MonoTODO]
public override IEnumerator GetEnumerator ()
{
- throw new NotImplementedException ();
+ return new DbEnumerator (this);
}
- [MonoTODO]
- public override Type GetFieldProviderSpecificType (int i)
+ public override Type GetProviderSpecificFieldType (int i)
{
- throw new NotImplementedException ();
+ return GetFieldType (i);
}
- [MonoTODO]
public override Type GetFieldType (int i)
{
- throw new NotImplementedException ();
+ ValidateClosed ();
+ return CurrentTable.Columns [i].DataType;
}
- [MonoTODO]
public override float GetFloat (int i)
{
- throw new NotImplementedException ();
+ return (float) GetValue (i);
}
- [MonoTODO]
public override Guid GetGuid (int i)
{
- throw new NotImplementedException ();
+ return (Guid) GetValue (i);
}
- [MonoTODO]
public override short GetInt16 (int i)
{
- throw new NotImplementedException ();
+ return (short) GetValue (i);
}
- [MonoTODO]
public override int GetInt32 (int i)
{
- throw new NotImplementedException ();
+ return (int) GetValue (i);
}
- [MonoTODO]
public override long GetInt64 (int i)
{
- throw new NotImplementedException ();
+ return (long) GetValue (i);
}
- [MonoTODO]
public override string GetName (int i)
{
- throw new NotImplementedException ();
+ ValidateClosed ();
+ return CurrentTable.Columns [i].ColumnName;
}
- [MonoTODO]
public override int GetOrdinal (string name)
{
- throw new NotImplementedException ();
+ ValidateClosed ();
+ int index = CurrentTable.Columns.IndexOf (name);
+ if (index == -1)
+ throw new ArgumentException (String.Format ("Column {0} is not found in the schema", name));
+ return index;
}
- [MonoTODO]
public override object GetProviderSpecificValue (int i)
{
- throw new NotImplementedException ();
+ return GetValue (i);
}
- [MonoTODO]
public override int GetProviderSpecificValues (object[] values)
{
- throw new NotImplementedException ();
- }
-
- [MonoTODO]
- public override DataTable GetSchemaTable ()
- {
- throw new NotImplementedException ();
+ return GetValues (values);
}
- [MonoTODO]
public override string GetString (int i)
{
- throw new NotImplementedException ();
+ return (string) GetValue (i);
}
- [MonoTODO]
public override object GetValue (int i)
{
- throw new NotImplementedException ();
+ return this [i];
}
- [MonoTODO]
public override int GetValues (object[] values)
{
- throw new NotImplementedException ();
+ Validate ();
+ if (CurrentRow.RowState == DataRowState.Deleted)
+ throw new DeletedRowInaccessibleException ("");
+
+ int count = (FieldCount < values.Length ? FieldCount : values.Length);
+ for (int i=0; i < count; ++i)
+ values [i] = CurrentRow [i];
+ return count;
}
- [MonoTODO]
public override bool IsDBNull (int i)
{
- throw new NotImplementedException ();
+ return GetValue (i) is DBNull;
+ }
+
+ public override DataTable GetSchemaTable ()
+ {
+ ValidateClosed ();
+ ValidateSchemaIntact ();
+
+ if (_schemaTable != null)
+ return _schemaTable;
+
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("ColumnName", typeof (string));
+ dt.Columns.Add ("ColumnOrdinal", typeof (int));
+ dt.Columns.Add ("ColumnSize", typeof (int));
+ dt.Columns.Add ("NumericPrecision", typeof (short));
+ dt.Columns.Add ("NumericScale", typeof (short));
+ dt.Columns.Add ("DataType", typeof (Type));
+ dt.Columns.Add ("ProviderType", typeof (int));
+ dt.Columns.Add ("IsLong", typeof (bool));
+ dt.Columns.Add ("AllowDBNull", typeof (bool));
+ dt.Columns.Add ("IsReadOnly", typeof (bool));
+ dt.Columns.Add ("IsRowVersion", typeof (bool));
+ dt.Columns.Add ("IsUnique", typeof (bool));
+ dt.Columns.Add ("IsKey", typeof (bool));
+ dt.Columns.Add ("IsAutoIncrement", typeof (bool));
+ dt.Columns.Add ("BaseCatalogName", typeof (string));
+ dt.Columns.Add ("BaseSchemaName", typeof (string));
+ dt.Columns.Add ("BaseTableName", typeof (string));
+ dt.Columns.Add ("BaseColumnName", typeof (string));
+ dt.Columns.Add ("AutoIncrementSeed", typeof (Int64));
+ dt.Columns.Add ("AutoIncrementStep", typeof (Int64));
+ dt.Columns.Add ("DefaultValue", typeof (object));
+ dt.Columns.Add ("Expression", typeof (string));
+ dt.Columns.Add ("ColumnMapping", typeof (MappingType));
+ dt.Columns.Add ("BaseTableNamespace", typeof (string));
+ dt.Columns.Add ("BaseColumnNamespace", typeof (string));
+
+ DataRow row;
+ DataColumn col;
+ for (int i=0; i < CurrentTable.Columns.Count; ++i) {
+ row = dt.NewRow ();
+ col = CurrentTable.Columns [i];
+ row ["ColumnName"] = col.ColumnName;
+ row ["BaseColumnName"] = col.ColumnName;
+ row ["ColumnOrdinal"] = col.Ordinal;
+ row ["ColumnSize"] = col.MaxLength;
+ // ms.net doesent set precision and scale even for Decimal values
+ // when are these set ?
+ row ["NumericPrecision"] = DBNull.Value;
+ row ["NumericScale"] = DBNull.Value;
+ row ["DataType"] = col.DataType;
+ row ["ProviderType"] = DBNull.Value; //col.ProviderType;
+ // ms.net doesent set this when datatype is string and maxlength = -1
+ // when is this set ?
+ row ["IsLong"] = false;
+ row ["AllowDBNull"] = col.AllowDBNull;
+ row ["IsReadOnly"] = col.ReadOnly;
+ row ["IsRowVersion"] = false; //this is always false
+ row ["IsUnique"] = col.Unique;
+ row ["IsKey"] = (Array.IndexOf (CurrentTable.PrimaryKey, col) != -1) ;
+ row ["IsAutoIncrement"]= col.AutoIncrement;
+ row ["AutoIncrementSeed"] = col.AutoIncrementSeed;
+ row ["AutoIncrementStep"] = col.AutoIncrementStep;
+ row ["BaseCatalogName"] = (CurrentTable.DataSet != null ? CurrentTable.DataSet.DataSetName : null);
+ row ["BaseSchemaName"] = DBNull.Value; // this is always null
+ row ["BaseTableName"] = CurrentTable.TableName;
+ row ["DefaultValue"] = col.DefaultValue;
+ // If col expression depends on any external table , then the
+ // Expression value is set to empty string in the schematable.
+ if (col.Expression == "")
+ row ["Expression"] = col.Expression;
+ else {
+ Regex reg = new Regex ("((Parent|Child)( )*[.(])", RegexOptions.IgnoreCase);
+ if (reg.IsMatch (col.Expression, 0))
+ row ["Expression"] = DBNull.Value;
+ else
+ row ["Expression"] = col.Expression;
+ }
+
+ row ["ColumnMapping"] = col.ColumnMapping;
+ row ["BaseTableNamespace"] = CurrentTable.Namespace;
+ row ["BaseColumnNamespace"] = col.Namespace;
+ dt.Rows.Add (row);
+ }
+
+ return _schemaTable = dt;
+ }
+
+ private void Validate ()
+ {
+ ValidateClosed ();
+
+ if (_index >= _tables.Length)
+ throw new InvalidOperationException ("Invalid attempt to read when no data is present");
+ if (_tableCleared)
+ throw new RowNotInTableException ("The table is cleared, no rows are accessible");
+ if (_current == -1)
+ throw new InvalidOperationException ("DataReader is invalid for the DataTable");
+ ValidateSchemaIntact ();
+ }
+
+ private void ValidateClosed ()
+ {
+ if (IsClosed)
+ throw new InvalidOperationException ("Invalid attempt to read when the reader is closed");
+ }
+
+ private void ValidateSchemaIntact ()
+ {
+ if (_schemaChanged)
+ throw new InvalidOperationException ("Schema of current DataTable '" + CurrentTable.TableName +
+ "' in DataTableReader has changed, DataTableReader is invalid.");
+ }
+
+ void ThrowInvalidCastException (Type sourceType, Type destType)
+ {
+ throw new InvalidCastException (
+ String.Format ("Unable to cast object of type '{0}' to type '{1}'.", sourceType, destType));
+ }
+
+ private bool MoveNext ()
+ {
+ if (_index >= _tables.Length || _tableCleared)
+ return false;
+
+ do {
+ _current++;
+ } while (_current < CurrentTable.Rows.Count && CurrentRow.RowState == DataRowState.Deleted);
+
+ _rowRef = _current < CurrentTable.Rows.Count ? CurrentRow : null;
+
+ return _current < CurrentTable.Rows.Count;
+
}
- [MonoTODO]
public override bool NextResult ()
{
- throw new NotImplementedException ();
+ if ((_index + 1) >= _tables.Length) {
+ UnsubscribeEvents ();
+ _index = _tables.Length; // to make any attempt invalid
+ return false; // end of tables.
+ }
+
+ UnsubscribeEvents ();
+ _index++;
+ _current = -1;
+ _rowRef = null;
+ _schemaTable = null; // force to create fresh
+ _tableCleared = false;
+ SubscribeEvents ();
+ return true;
}
- [MonoTODO]
public override bool Read ()
{
- throw new NotImplementedException ();
+ ValidateClosed ();
+ return MoveNext ();
}
#endregion // Methods
+
+ #region // Event Handlers
+
+ private void OnColumnChanged (object sender, PropertyChangedEventArgs args)
+ {
+ _schemaChanged = true;
+ }
+
+ private void OnColumnCollectionChanged (object sender, CollectionChangeEventArgs args)
+ {
+ _schemaChanged = true;
+ }
+
+ private void OnRowChanged (object src, DataRowChangeEventArgs args)
+ {
+ DataRowAction action = args.Action;
+ DataRow row = args.Row;
+ if (action == DataRowAction.Add) {
+ if (_tableCleared && _current != -1)
+ return;
+
+ if (_current == -1 || (_current >= 0 && row.RowID > CurrentRow.RowID)) {
+ _tableCleared = false;
+ return; // no. movement required, if row added after current.
+ }
+
+ _current++;
+ _rowRef = CurrentRow;
+ }
+
+ if (action == DataRowAction.Commit && row.RowState == DataRowState.Detached) {
+ // if i am the row deleted, move one down
+ if (_rowRef == row) {
+ _current --;
+ _rowRef = _current >= 0 ? CurrentRow : null;
+ }
+
+ // if the row deleted is the last row, move down
+ if (_current >= CurrentTable.Rows.Count) {
+ _current--;
+ _rowRef = _current >= 0 ? CurrentRow : null;
+ return;
+ }
+
+ // deleting a row below _current moves the row one down
+ if (_current > 0 && _rowRef == CurrentTable.Rows [_current-1]) {
+ _current--;
+ _rowRef = CurrentRow;
+ return;
+ }
+ }
+ }
+
+ private void OnTableCleared (object src, DataTableClearEventArgs args)
+ {
+ _tableCleared = true;
+ }
+
+ #endregion // Event Handlers
}
}