copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / System.Data / System.Data / DataTableReader.cs
index ddd0bb17b1f16779078b1536c995a8a77040c924..a1b5ac11daa27b69a40589d2e021e8bbcadc7df5 100644 (file)
@@ -2,7 +2,8 @@
 // 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
@@ -67,222 +85,460 @@ namespace System.Data {
                }
 
                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
        }
 }