* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.Data / System.Data / DataTableReader.cs
1 //
2 // System.Data.DataTableReader.cs
3 //
4 // Author:
5 //   Sureshkumar T      <tsureshkumar@novell.com>
6 //   Tim Coleman        (tim@timcoleman.com)
7 //
8 // Copyright (C) Tim Coleman, 2003
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 #if NET_2_0
35
36 using System.Collections;
37 using System.Data.Common;
38 using System.ComponentModel;
39 using System.Text.RegularExpressions;
40
41 namespace System.Data {
42         public sealed class DataTableReader : DbDataReader
43         {
44                 bool            _closed;
45                 DataTable []    _tables;
46                 int             _current = -1;
47                 int             _index;
48                 DataTable       _schemaTable;
49                 bool            _tableCleared = false;
50                 bool            _subscribed = false;
51                 DataRow         _rowRef;
52                 bool            _schemaChanged = false;
53
54                 #region Constructors
55                 
56                 public DataTableReader (DataTable dt)
57                         : this (new DataTable[] {dt})
58                 {
59                 }
60                 
61                 public DataTableReader (DataTable[] dataTables)
62                 {
63                         if (dataTables == null 
64                             || dataTables.Length <= 0)
65                                 throw new ArgumentException ("Cannot Create DataTable. Argument Empty!");
66
67                         this._tables = new DataTable [dataTables.Length];
68
69                         for (int i = 0; i < dataTables.Length; i++)
70                                 this._tables [i] = dataTables [i];
71                         
72                         _closed = false;
73                         _index = 0;
74                         _current = -1;
75                         _rowRef = null;
76                         _tableCleared = false;
77
78                         SubscribeEvents ();
79                 }
80
81                 #endregion // Constructors
82
83                 #region Properties
84
85                 public override int Depth {
86                         get { return 0; }
87                 }
88
89                 public override int FieldCount {
90                         get { return CurrentTable.Columns.Count; }
91                 }
92
93                 public override bool HasRows {
94                         get { return CurrentTable.Rows.Count > 0; }
95                 }
96
97                 public override bool IsClosed {
98                         get { return _closed; }
99                 }
100                 
101                 public override object this [int index] {
102                         get { 
103                                 Validate ();
104                                 if (index < 0 || index >= FieldCount)
105                                         throw new ArgumentOutOfRangeException ("index " + index + " is not in the range");
106                                 DataRow row = CurrentRow;
107                                 if (row.RowState == DataRowState.Deleted)
108                                         throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
109                                 return row [index];
110                         }
111                 }
112
113                 private DataTable CurrentTable
114                 {
115                         get { return _tables [_index]; }
116                 }
117                 
118                 private DataRow CurrentRow
119                 {
120                         get { return (DataRow) CurrentTable.Rows [_current]; }
121                 }
122
123                 
124                 public override object this [string name] {
125                         get {
126                                 Validate ();
127                                 DataRow row = CurrentRow;
128                                 if (row.RowState == DataRowState.Deleted)
129                                         throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
130                                 return row [name];
131                         }
132                 }
133                 
134                 public override int RecordsAffected {
135                         get { return 0; }
136                 }
137                 
138                 #endregion // Properties
139
140                 #region Methods
141
142                 private void SubscribeEvents ()
143                 {
144                         if (_subscribed)        // avoid subscribing multiple times
145                                 return;
146                         CurrentTable.TableCleared += new DataTableClearEventHandler (OnTableCleared);
147                         CurrentTable.RowChanged += new DataRowChangeEventHandler (OnRowChanged);
148                         CurrentTable.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnCollectionChanged);
149                         for (int i=0; i < CurrentTable.Columns.Count; ++i)
150                                 CurrentTable.Columns [i].PropertyChanged += new PropertyChangedEventHandler (OnColumnChanged);
151                         _subscribed = true;
152                         _schemaChanged = false;
153                 }
154
155                 private void UnsubscribeEvents ()
156                 {
157                         if (!_subscribed)       // avoid un-subscribing multiple times
158                                 return;
159                         CurrentTable.TableCleared -= new DataTableClearEventHandler (OnTableCleared);
160                         CurrentTable.RowChanged -= new DataRowChangeEventHandler (OnRowChanged);
161                         CurrentTable.Columns.CollectionChanged -= new CollectionChangeEventHandler (OnColumnCollectionChanged);
162                         for (int i=0; i < CurrentTable.Columns.Count; ++i)
163                                 CurrentTable.Columns [i].PropertyChanged -= new PropertyChangedEventHandler (OnColumnChanged);
164                         _subscribed = false;
165                         _schemaChanged = false;
166                 }
167                 
168                 public override void Close ()
169                 {
170                         if (IsClosed)
171                                 return;
172                         
173                         UnsubscribeEvents ();
174                         _closed = true;
175                 }
176                 
177                 public override bool GetBoolean (int i)
178                 {
179                         return (bool) GetValue (i);
180                 }
181                 
182                 public override byte GetByte (int i)
183                 {
184                         return (byte) GetValue (i);
185                 }
186
187                 public override long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
188                 {
189                         byte[] value = this [i] as byte[];
190                         if (value == null)
191                                 ThrowInvalidCastException (this [i].GetType (), typeof (byte[]));
192                         if (buffer == null)
193                                 return value.Length;
194                         int copylen = length > value.Length ? value.Length : length;
195                         Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
196                         return copylen;
197                 }
198                 
199                 public override char GetChar (int i)
200                 {
201                         return (char) GetValue (i);
202                 }
203
204                 public override long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
205                 {
206                         char[] value = this [i] as char[];
207                         if (value == null)
208                                 ThrowInvalidCastException (this [i].GetType (), typeof (char[]));
209                         if (buffer == null)
210                                 return value.Length;
211                         int copylen = length > value.Length ? value.Length : length;
212                         Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
213                         return copylen;
214                 }
215                 
216                 public override string GetDataTypeName (int i)
217                 {
218                         return GetFieldType (i).ToString ();
219                 }
220                 
221                 public override DateTime GetDateTime (int i)
222                 {
223                         return (DateTime) GetValue (i);
224                 }
225                 
226                 public override decimal GetDecimal (int i)
227                 {
228                         return (decimal) GetValue (i);
229                 }
230                 
231                 public override double GetDouble (int i)
232                 {
233                         return (double) GetValue (i);
234                 }
235
236                 public override IEnumerator GetEnumerator ()
237                 {
238                         return new DbEnumerator (this);
239                 }
240
241                 public override Type GetProviderSpecificFieldType (int i)
242                 {
243                         return GetFieldType (i);
244                 }
245                 
246                 public override Type GetFieldType (int i)
247                 {
248                         ValidateClosed ();
249                         return CurrentTable.Columns [i].DataType;
250                 }
251                 
252                 public override float GetFloat (int i)
253                 {
254                         return (float) GetValue (i);
255                 }
256                 
257                 public override Guid GetGuid (int i)
258                 {
259                         return (Guid) GetValue (i);
260                 }
261                 
262                 public override short GetInt16 (int i)
263                 {
264                         return (short) GetValue (i);
265                 }
266                 
267                 public override int GetInt32 (int i)
268                 {
269                         return (int) GetValue (i);
270                 }
271                 
272                 public override long GetInt64 (int i)
273                 {
274                         return (long) GetValue (i);
275                 }
276                 
277                 public override string GetName (int i)
278                 {
279                         ValidateClosed ();
280                         return CurrentTable.Columns [i].ColumnName;
281                 }
282                 
283                 public override int GetOrdinal (string name)
284                 {
285                         ValidateClosed ();
286                         int index = CurrentTable.Columns.IndexOf (name);
287                         if (index == -1)
288                                 throw new ArgumentException (String.Format ("Column {0} is not found in the schema",
289                                                                         name));
290                         return index;
291                 }
292
293                 public override object GetProviderSpecificValue (int i)
294                 {
295                         return GetValue (i);
296                 }
297
298                 public override int GetProviderSpecificValues (object[] values)
299                 {
300                         return GetValues (values);
301                 }
302                 
303                 public override string GetString (int i)
304                 {
305                         return (string) GetValue (i);
306                 }
307                 
308                 public override object GetValue (int i)
309                 {
310                         return this [i];
311                 }
312
313                 public override int GetValues (object[] values)
314                 {
315                         Validate ();
316                         if (CurrentRow.RowState == DataRowState.Deleted)
317                                 throw new DeletedRowInaccessibleException ("");
318
319                         int count = (FieldCount < values.Length ? FieldCount : values.Length);
320                         for (int i=0; i < count; ++i)
321                                 values [i] = CurrentRow [i];
322                         return count;
323                 }
324                 
325                 public override bool IsDBNull (int i)
326                 {
327                         return GetValue (i) is DBNull;
328                 }
329                 
330                 public override DataTable GetSchemaTable ()
331                 {
332                         ValidateClosed ();
333                         ValidateSchemaIntact ();
334
335                         if (_schemaTable != null)
336                                 return _schemaTable;
337                         
338                         DataTable dt = new DataTable ();
339                         DataColumn[] cols = new DataColumn [25];
340                         dt.Columns.Add ("ColumnName", typeof (string));
341                         dt.Columns.Add ("ColumnOrdinal", typeof (int));
342                         dt.Columns.Add ("ColumnSize", typeof (int));
343                         dt.Columns.Add ("NumericPrecision", typeof (short));
344                         dt.Columns.Add ("NumericScale", typeof (short));
345                         dt.Columns.Add ("DataType", typeof (Type));
346                         dt.Columns.Add ("ProviderType", typeof (int));
347                         dt.Columns.Add ("IsLong", typeof (bool));
348                         dt.Columns.Add ("AllowDBNull", typeof (bool));
349                         dt.Columns.Add ("IsReadOnly", typeof (bool));
350                         dt.Columns.Add ("IsRowVersion", typeof (bool));
351                         dt.Columns.Add ("IsUnique", typeof (bool));
352                         dt.Columns.Add ("IsKey", typeof (bool));
353                         dt.Columns.Add ("IsAutoIncrement", typeof (bool));
354                         dt.Columns.Add ("BaseCatalogName", typeof (string));
355                         dt.Columns.Add ("BaseSchemaName", typeof (string));
356                         dt.Columns.Add ("BaseTableName", typeof (string));
357                         dt.Columns.Add ("BaseColumnName", typeof (string));
358                         dt.Columns.Add ("AutoIncrementSeed", typeof (Int64));
359                         dt.Columns.Add ("AutoIncrementStep", typeof (Int64));
360                         dt.Columns.Add ("DefaultValue", typeof (object));
361                         dt.Columns.Add ("Expression", typeof (string));
362                         dt.Columns.Add ("ColumnMapping", typeof (MappingType));
363                         dt.Columns.Add ("BaseTableNamespace", typeof (string));
364                         dt.Columns.Add ("BaseColumnNamespace", typeof (string));
365
366                         DataRow row;
367                         DataColumn col;
368                         for (int i=0; i < CurrentTable.Columns.Count; ++i) {
369                                 row = dt.NewRow ();
370                                 col = CurrentTable.Columns [i];
371                                 row ["ColumnName"] = col.ColumnName;
372                                 row ["BaseColumnName"] = col.ColumnName;
373                                 row ["ColumnOrdinal"] = col.Ordinal;
374                                 row ["ColumnSize"] = col.MaxLength;
375                                 // ms.net doesent set precision and scale even for Decimal values
376                                 // when are these set ?
377                                 row ["NumericPrecision"] = DBNull.Value;
378                                 row ["NumericScale"] = DBNull.Value;
379                                 row ["DataType"] = col.DataType;
380                                 row ["ProviderType"] = DBNull.Value; //col.ProviderType;
381                                 // ms.net doesent set this when datatype is string and maxlength = -1
382                                 // when is this set ? 
383                                 row ["IsLong"] = false;
384                                 row ["AllowDBNull"] = col.AllowDBNull;
385                                 row ["IsReadOnly"] = col.ReadOnly;
386                                 row ["IsRowVersion"] = false; //this is always false
387                                 row ["IsUnique"] = col.Unique;
388                                 row ["IsKey"] = (Array.IndexOf (CurrentTable.PrimaryKey, col) != -1) ;
389                                 row ["IsAutoIncrement"]= col.AutoIncrement;
390                                 row ["AutoIncrementSeed"] = col.AutoIncrementSeed;
391                                 row ["AutoIncrementStep"] = col.AutoIncrementStep;
392                                 row ["BaseCatalogName"] = (CurrentTable.DataSet != null ? CurrentTable.DataSet.DataSetName : null);
393                                 row ["BaseSchemaName"] = DBNull.Value; // this is always null
394                                 row ["BaseTableName"] = CurrentTable.TableName;
395                                 row ["DefaultValue"] = col.DefaultValue;
396                                 // If col expression depends on any external table , then the
397                                 // Expression value is set to empty string in the schematable.
398                                 if (col.Expression == "")
399                                         row ["Expression"] = col.Expression;
400                                 else {
401                                         Regex reg = new Regex ("((Parent|Child)( )*[.(])", RegexOptions.IgnoreCase);
402                                         if (reg.IsMatch (col.Expression, 0))
403                                                 row ["Expression"] = DBNull.Value;
404                                         else
405                                                 row ["Expression"] = col.Expression;
406                                 }
407
408                                 row ["ColumnMapping"] = col.ColumnMapping;
409                                 row ["BaseTableNamespace"] = CurrentTable.Namespace;
410                                 row ["BaseColumnNamespace"] = col.Namespace;
411                                 dt.Rows.Add (row);
412                         }
413
414                         return _schemaTable = dt;
415                 }
416
417                 private void Validate ()
418                 {
419                         ValidateClosed ();
420
421                         if (_index >= _tables.Length)
422                                 throw new InvalidOperationException ("Invalid attempt to read when " + 
423                                                                      "no data is present");
424                         if (_tableCleared)
425                                 throw new RowNotInTableException ("The table is cleared, no rows are " +
426                                                                   "accessible");
427                         if (_current == -1)
428                                 throw new InvalidOperationException ("DataReader is invalid " + 
429                                                                      "for the DataTable");
430                         ValidateSchemaIntact ();
431                 }
432
433                 private void ValidateClosed ()
434                 {
435                         if (IsClosed)
436                                 throw new InvalidOperationException ("Invalid attempt to read when " + 
437                                                                      "the reader is closed");
438                 }
439
440                 private void ValidateSchemaIntact ()
441                 {
442                         if (_schemaChanged)
443                                 throw new InvalidOperationException ("Schema of current DataTable '" + CurrentTable.TableName +
444                                                 "' in DataTableReader has changed, DataTableReader is invalid.");
445                 }
446
447                 void ThrowInvalidCastException (Type sourceType, Type destType)
448                 {
449                         throw new InvalidCastException (String.Format ("Unable to cast object of type '{0}' to type '{1}'.", 
450                                                                 sourceType, destType));
451                 }
452                 
453                 private bool MoveNext ()
454                 {
455                         if (_index >= _tables.Length || _tableCleared)
456                                 return false;
457                         
458                         do {
459                                 _current++;
460                         } while (_current < CurrentTable.Rows.Count 
461                                  && CurrentRow.RowState == DataRowState.Deleted);
462                         
463                         _rowRef = _current < CurrentTable.Rows.Count ? CurrentRow : null;
464
465                         return _current < CurrentTable.Rows.Count;
466
467                 }
468
469                 public override bool NextResult ()
470                 {
471                         if ((_index + 1) >= _tables.Length) {
472                                 UnsubscribeEvents ();
473                                 _index = _tables.Length;     // to make any attempt invalid
474                                 return false; // end of tables.
475                         }
476                         
477                         UnsubscribeEvents ();
478                         _index++;
479                         _current = -1;
480                         _rowRef = null;
481                         _schemaTable = null;            // force to create fresh
482                         _tableCleared = false;
483                         SubscribeEvents ();
484                         return true;
485                 }
486
487                 public override bool Read ()
488                 {       
489                         ValidateClosed ();
490                         return MoveNext ();
491                 }
492
493                 #endregion // Methods
494
495                 #region // Event Handlers
496                 
497                 private void OnColumnChanged (object sender, PropertyChangedEventArgs args)
498                 {
499                         _schemaChanged = true;
500                 }
501
502                 private void OnColumnCollectionChanged (object sender, CollectionChangeEventArgs args) 
503                 {
504                         _schemaChanged = true;
505                 }
506
507                 private void OnRowChanged (object src, DataRowChangeEventArgs args)
508                 {
509                         DataRowAction action = args.Action;
510                         DataRow row = args.Row;
511                         if (action == DataRowAction.Add) {
512                                 if (_tableCleared && _current != -1)
513                                         return;
514                                 
515                                 if (_current == -1 // yet to read
516                                     || (_current >= 0 && row.RowID > CurrentRow.RowID) // row added above
517                                     ) {
518                                         _tableCleared = false;
519                                         return; // no. movement required, if row added after current.
520                                 }
521
522                                 _current++;
523                                 _rowRef = CurrentRow;
524
525                         }
526                         
527                         if (action == DataRowAction.Commit 
528                             && row.RowState == DataRowState.Detached) {
529
530                                 // if i am the row deleted, move one down
531                                 if (_rowRef == row) {
532                                         _current --;
533                                         _rowRef = _current >= 0 ? CurrentRow : null;
534                                 }
535
536                                 // if the row deleted is the last row, move down
537                                 if (_current >= CurrentTable.Rows.Count) {
538                                         _current--;
539                                         _rowRef = _current >= 0 ? CurrentRow : null;
540                                         return;
541                                 }
542                                 
543                                 // deleting a row below _current moves the row one down
544                                 if (_current > 0 && _rowRef == CurrentTable.Rows [_current-1]) {
545                                         _current--;
546                                         _rowRef = CurrentRow;
547                                         return;
548                                 }
549                         }
550                 }
551
552                 private void OnTableCleared (object src, DataTableClearEventArgs args)
553                 {
554                         _tableCleared = true;
555                 }
556                 
557                 #endregion // Event Handlers
558         }
559 }
560
561 #endif // NET_2_0