2004-03-31 Juraj Skripsky <juraj@hotfeet.ch>
[mono.git] / mcs / class / System.Data / System.Data / DataView.cs
1 //
2 // System.Data.DataView.cs
3 //
4 // Author:
5 //    Daniel Morgan <danmorg@sc.rr.com>
6 //    Tim Coleman (tim@timcoleman.com)
7 //
8 // Copyright (C) Daniel Morgan, 2002, 2003
9 // (C) Ximian, Inc 2002
10 // Copyright (C) Tim Coleman, 2002-2003
11 //
12
13 using System;
14 using System.Collections;
15 using System.ComponentModel;
16 using System.Reflection;
17
18 namespace System.Data 
19 {
20         /// <summary>
21         /// A DataView is used in the binding of data between
22         /// a DataTable and Windows Forms or Web Forms allowing
23         /// a view of a DataTable for editing, filtering,
24         /// navigation, searching, and sorting.
25         /// </summary>
26         //[Designer]
27         [Editor]
28         [DefaultEvent ("PositionChanged")]
29         [DefaultProperty ("Table")]
30         public class DataView : MarshalByValueComponent, IBindingList, IList, ICollection, IEnumerable, ITypedList, ISupportInitialize
31         {
32                 DataTable dataTable = null;
33                 string rowFilter = "";
34                 string sort = "";
35                 DataViewRowState rowState;
36                 DataRowView[] rowCache = null;
37                 
38                 // FIXME: what are the default values?
39                 bool allowNew = true; 
40                 bool allowEdit = true;
41                 bool allowDelete = true;
42                 bool applyDefaultSort = false;
43                 bool isSorted = false;
44
45                 bool isOpen = false;
46
47                 bool bInit = false;
48                 
49                 internal DataViewManager dataViewManager = null;
50
51                 public DataView () 
52                 {
53                         dataTable = new DataTable ();
54                         rowState = DataViewRowState.CurrentRows;
55                         Open ();
56                 }
57
58                 public DataView (DataTable table) 
59                 {
60                         dataTable = table;
61                         rowState = DataViewRowState.CurrentRows;
62                         Open ();
63                 }
64
65                 public DataView (DataTable table, string RowFilter,
66                                 string Sort, DataViewRowState RowState) 
67                 {
68                         dataTable = table;
69                         rowState = DataViewRowState.CurrentRows;
70                         rowFilter = RowFilter;
71                         sort = Sort;
72                         rowState = RowState;
73                         Open();
74                 }
75
76                 [DataCategory ("Data")]
77                 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows deletes.")]
78                 [DefaultValue (true)]
79                 public bool AllowDelete {
80                         get {
81                                 return allowDelete;
82                         }
83                         set {
84                                 allowDelete = value;
85                         }
86                 }
87
88                 [DataCategory ("Data")]
89                 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows edits.")]
90                 [DefaultValue (true)]
91                 public bool AllowEdit {
92                         get {
93                                 return allowEdit;
94                         }
95                         set {
96                                 allowEdit = value;
97                         }
98                 }
99
100                 [DataCategory ("Data")]
101                 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows new rows to be added.")]
102                 [DefaultValue (true)]
103                 public bool AllowNew {
104                         get {
105                                 return allowNew;
106                         }
107                         
108                         set {
109                                 allowNew = value;
110                         }
111                 }
112
113                 [DataCategory ("Data")]
114                 [DataSysDescription ("Indicates whether to use the default sort if the Sort property is not set.")]
115                 [DefaultValue (false)]
116                 [RefreshProperties (RefreshProperties.All)]
117                 public bool ApplyDefaultSort {
118                         [MonoTODO]
119                         get {                           
120                                 return applyDefaultSort;
121                         }
122                         
123                         [MonoTODO]
124                         set {
125                                 applyDefaultSort = value;
126                                 // FIXME: update the index cache to the DataTable, and 
127                                 //        only refresh the index when the DataTable
128                                 //        has changes via column, row, or constraint
129                                 //        changed events
130                                 UpdateIndex ();
131                         }
132                 }
133
134                 // get the count of rows in the DataView after RowFilter 
135                 // and RowStateFilter have been applied
136                 [Browsable (false)]
137                 [DataSysDescription ("Returns the number of items currently in this view.")]
138                 public int Count {
139                         [MonoTODO]
140                         get {
141                                 // FIXME: remove this line once collection change
142                                 //        events from the DataTable are handled
143                                 UpdateIndex ();
144
145                                 return rowCache.Length;;
146                         }
147                 }
148
149                 [Browsable (false)]
150                 [DataSysDescription ("This returns a pointer to back to the DataViewManager that owns this DataSet (if any).")]
151                 public DataViewManager DataViewManager {
152                         [MonoTODO]
153                         get {
154                                 return dataViewManager;
155                         }
156                 }
157
158                 // Item indexer
159                 // the compiler creates a DefaultMemeberAttribute from
160                 // this IndexerNameAttribute
161                 [System.Runtime.CompilerServices.IndexerName("Item")]
162                 public DataRowView this[int recordIndex] {
163                         [MonoTODO]
164                         get {
165                                 // FIXME: use index cache to the DataTable, and 
166                                 //        only refresh the index when the DataTable
167                                 //        has changes via column, row, or constraint
168                                 //        changed events
169                                 // Remove this line once changed events are handled
170                                 UpdateIndex ();
171                                 
172                                 return rowCache[recordIndex];
173                         }
174                 }
175
176                 [DataCategory ("Data")]
177                 [DataSysDescription ("Indicates an expression used to filter the data returned by this DataView.")]
178                 [DefaultValue ("")]
179                 public virtual string RowFilter {
180                         [MonoTODO]
181                         get {
182                                 return rowFilter;
183                         }
184                         
185                         [MonoTODO]
186                         set {
187                                 rowFilter = value;
188                                 // FIXME: update the index cache to the DataTable, and 
189                                 //        only refresh the index when the DataTable
190                                 //        has changes via column, row, or constraint
191                                 //        changed events
192                                 UpdateIndex ();
193                         }
194                 }
195
196                 [DataCategory ("Data")]
197                 [DataSysDescription ("Indicates the versions of data returned by this DataView.")]
198                 [DefaultValue (DataViewRowState.CurrentRows)]
199                 public DataViewRowState RowStateFilter {
200                         [MonoTODO]
201                         get {
202                                 return rowState;
203                         }
204                         
205                         [MonoTODO]
206                         set {
207                                 rowState = value;
208                                 // FIXME: update the index cache to the DataTable, and 
209                                 //        only refresh the index when the DataTable
210                                 //        has changes via column, row, or constraint
211                                 //        changed events
212                                 UpdateIndex ();
213                         }
214                 }
215
216                 [DataCategory ("Data")]
217                 [DataSysDescription ("Indicates the order in which data is returned by this DataView.")]
218                 [DefaultValue ("")]
219                 public string Sort {
220                         [MonoTODO]
221                         get {
222                                 return sort;
223                         }
224                         
225                         [MonoTODO]
226                         set {
227                                 sort = value;
228                                 // FIXME: update the index cache to the DataTable, and 
229                                 //        only refresh the index when the DataTable
230                                 //        has changes via column, row, or constraint
231                                 //        changed events
232                                 UpdateIndex ();
233                         }
234                 }
235
236                 [DataCategory ("Data")]
237                 [DataSysDescription ("Indicates the table this DataView uses to get data.")]
238                 [DefaultValue (null)]
239                 [RefreshProperties (RefreshProperties.All)]
240                 public DataTable Table {
241                         [MonoTODO]
242                         get {
243                                 return dataTable;
244                         }
245                         
246                         [MonoTODO]
247                         set {
248                                 dataTable = value;
249                                 // FIXME: update the index cache to the DataTable, and 
250                                 //        only refresh the index when the DataTable
251                                 //        has changes via column, row, or constraint
252                                 //        changed events
253                                 UpdateIndex ();
254                         }
255                 }
256
257                 [MonoTODO]
258                 public virtual DataRowView AddNew() 
259                 {
260                         if (!IsOpen)
261                                 throw new DataException ("DataView is not open.");
262                         if (!AllowNew)
263                                 throw new DataException ("Cannot call AddNew on a DataView where AllowNew is false.");
264                         
265                         DataRow row = dataTable.NewRow ();
266                         DataRowView view = new DataRowView (this, row);
267                         
268                         dataTable.Rows.Add (row);
269
270                         UpdateIndex ();
271                         OnListChanged (new ListChangedEventArgs (ListChangedType.ItemAdded, rowCache.Length));
272                         return view;
273                 }
274
275                 [MonoTODO]
276                 public void BeginInit() 
277                 {
278                         bInit = true; 
279                         // FIXME:
280                 }
281
282                 [MonoTODO]
283                 public void CopyTo (Array array, int index) 
284                 {
285                         // FIXME: use index cache to the DataTable, and 
286                         //        only refresh the index when the DataTable
287                         //        has changes via column, row, or constraint
288                         //        changed events
289                         UpdateIndex ();
290                         
291                         int row = 0;
292                         for (; row < rowCache.Length && row < array.Length; row++) {
293                                 array.SetValue (rowCache[row], index + row);
294                         }
295                         if (row < array.Length) {
296                                 for (int r = 0; r < array.Length; r++) {
297                                         array.SetValue (null, index + r);
298                                 }
299                         }
300                 }
301
302                 public void Delete(int index) 
303                 {
304                         if (!IsOpen)
305                                 throw new DataException ("DataView is not open.");
306                         if (!AllowDelete)
307                                 throw new DataException ("Cannot delete on a DataSource where AllowDelete is false.");
308                         
309                         UpdateIndex ();
310
311                         if (index > rowCache.Length)
312                                 throw new IndexOutOfRangeException ("There is no row at " +
313                                                 "position: " + index + ".");
314                         
315                         DataRowView row = rowCache [index];
316                         row.Row.Delete ();
317
318                         OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, index));
319                 }
320
321 #if NET_1_2
322                 [MonoTODO]
323                 public virtual bool Equals (DataView dv)
324                 {
325                         throw new NotImplementedException ();
326                 }
327 #endif
328
329                 [MonoTODO]
330                 public void EndInit() 
331                 {
332                         bInit = false;
333                         // FIXME:
334                 }
335
336                 [MonoTODO]
337                 public int Find(object key) 
338                 {
339                         // FIXME: use index cache to the DataTable, and 
340                         //        only refresh the index when the DataTable
341                         //        has changes via column, row, or constraint
342                         //        changed events
343
344                         throw new NotImplementedException ();
345                 }
346
347                 [MonoTODO]
348                 public int Find(object[] key) 
349                 {
350                         // FIXME: use an index cache to the DataTable, and 
351                         //        only refresh the index when the DataTable
352                         //        has changes via column, row, or constraint
353                         //        changed events
354
355                         throw new NotImplementedException ();
356                 }
357
358                 [MonoTODO]
359                 public DataRowView[] FindRows(object key) 
360                 {
361                         // FIXME: use an index cache to the DataTable, and 
362                         //        only refresh the index when the DataTable
363                         //        has changes via column, row, or constraint
364                         //        changed events
365
366                         throw new NotImplementedException ();
367                 }
368
369                 [MonoTODO]
370                 public DataRowView[] FindRows(object[] key) 
371                 {
372                         // FIXME: use an index cache to the DataTable, and 
373                         //        only refresh the index when the DataTable
374                         //        has changes via column, row, or constraint
375                         //        changed events
376                         
377                         throw new NotImplementedException ();
378                 }
379
380                 [MonoTODO]
381                 public IEnumerator GetEnumerator() 
382                 {
383                         // FIXME: use an index cache to the DataTable, and 
384                         //        only refresh the index when the DataTable
385                         //        has changes via column, row, or constraint
386                         //        changed events
387                         UpdateIndex ();                                 
388
389                         return new DataViewEnumerator (rowCache);
390                 }
391                 
392                 [MonoTODO]
393                 [DataCategory ("Data")]
394                 [DataSysDescription ("Indicates the data returned by this DataView has somehow changed.")]
395                 public event ListChangedEventHandler ListChanged;
396
397                 protected bool IsOpen {
398                         [MonoTODO]
399                         get {
400                                 return isOpen;
401                         }
402                 }
403
404                 [MonoTODO]
405                 protected void Close() 
406                 {
407                         // FIXME:
408                         isOpen = false;
409                 }
410
411                 [MonoTODO]
412                 protected virtual void ColumnCollectionChanged (object sender, 
413                                                         CollectionChangeEventArgs e) 
414                 {
415                         throw new NotImplementedException ();
416                 }
417
418                 protected override void Dispose (bool disposing) 
419                 {
420                         if (disposing)
421                                 Close ();
422
423                         base.Dispose (disposing);
424                 }
425
426                 [MonoTODO]
427                 protected virtual void IndexListChanged(object sender, ListChangedEventArgs e) 
428                 {
429                         throw new NotImplementedException ();
430                 }
431
432                 [MonoTODO]
433                 protected virtual void OnListChanged(ListChangedEventArgs e) 
434                 {
435                         try {
436                                 if (ListChanged != null)
437                                         ListChanged (this, e);
438                         } catch {
439                         }
440                 }
441
442                 [MonoTODO]
443                 protected void Open() 
444                 {
445                         // FIXME: create the initial index cache to the DataTable, and 
446                         //        only refresh the index when the DataTable
447                         //        has changes via column, row, or constraint
448                         //        changed events. the index cache is generally
449                         //        a DataViewRow array that points to the actual
450                         //        DataRows in the this DataTable's DataRowCollection;
451                         //        this index is really a cache that gets 
452                         //        created during Open(), gets Updated 
453                         //        when various properties of this view
454                         //        changes, gets Updated when this DataTable's 
455                         //        row, column, or constraint collections have changed.
456                         //        I'm not sure what else.
457                         //        The data view will know one of the DataTable's
458                         //        collections have changed via one of 
459                         //        its changed events.
460                         //        Otherwise, if getting a/the DataRowView(s),
461                         //        Count, or other properties, then just use the
462                         //        index cache.
463                         UpdateIndex (true);
464                         isOpen = true;
465                 }
466
467                 // internal use by Mono
468                 protected void Reset() \r
469                 {\r
470                         // TODO: what really happens?\r
471                         Close ();\r
472                         rowCache = null;\r
473                         Open ();\r
474                         OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1));
475                 }\r
476
477 #if NET_1_2
478                 [MonoTODO]
479                 public DataTable ToTable ()
480                 {
481                         throw new NotImplementedException ();
482                 }
483
484                 [MonoTODO]
485                 public DataTable ToTable (bool isDistinct, string[] columnNames)
486                 {
487                         throw new NotImplementedException ();
488                 }
489 #endif
490
491                 // internal use by Mono
492                 protected virtual void UpdateIndex () \r
493                 {\r
494                         UpdateIndex (false);\r
495                 }\r
496
497                 // This is method is internal to 
498                 // the Mono implementation of DataView; it
499                 // is not to be used from your code.
500                 //
501                 // Update the DataRowView array which is an index cache
502                 // into the DataTable's DataRowCollection.
503                 //
504                 // I assume this is what UpdateIndex is used for
505                 protected virtual void UpdateIndex(bool force) 
506                 {
507                         DataRowView[] newRowCache = null;
508                         DataRow[] rows = null;
509                         
510                         rows = dataTable.Select (RowFilter, Sort, RowStateFilter);
511
512                         newRowCache = new DataRowView[rows.Length];
513                         for (int r = 0; r < rows.Length; r++) {
514                                 newRowCache[r] = new DataRowView (this, rows[r]);
515                         }
516                         rowCache = newRowCache;
517                 }
518
519                 [MonoTODO]
520                 PropertyDescriptorCollection ITypedList.GetItemProperties (PropertyDescriptor[] listAccessors) 
521                 {
522                         // FIXME: use listAccessors somehow
523
524                         DataColumnPropertyDescriptor[] descriptors = 
525                                 new DataColumnPropertyDescriptor[dataTable.Columns.Count];
526
527                         DataColumnPropertyDescriptor descriptor;
528                         DataColumn dataColumn;
529                         for (int col = 0; col < dataTable.Columns.Count; col ++)
530                         {
531                                 dataColumn = dataTable.Columns[col];
532                                 
533                                 descriptor = new DataColumnPropertyDescriptor(
534                                         dataColumn.ColumnName, col, null);
535                                 descriptor.SetComponentType (typeof (System.Data.DataRowView));
536                                 descriptor.SetPropertyType (dataColumn.DataType);
537                                 descriptor.SetReadOnly (dataColumn.ReadOnly);
538
539                                 descriptors[col] = descriptor;
540                         }
541
542                         return new PropertyDescriptorCollection (descriptors);
543                 }
544
545                 [MonoTODO]
546                 string ITypedList.GetListName (PropertyDescriptor[] listAccessors) 
547                 {
548                         return "";
549                 }
550
551                 //int ICollection.Count { 
552                 //      get {
553                 //              return Count;
554                 //      } 
555                 //}
556
557                 bool ICollection.IsSynchronized { 
558                         [MonoTODO]
559                         get {
560                                 return false;
561                         } 
562                 }
563
564                 object ICollection.SyncRoot { 
565                         [MonoTODO]
566                         get {
567                                 // FIXME:
568                                 return this;
569                         }
570                 }
571
572                 //void ICollection.CopyTo (Array array, int index) 
573                 //{
574                 //      CopyTo (array, index);
575                 //}
576
577                 bool IList.IsFixedSize {
578                         [MonoTODO]
579                         get {
580                                 return false;
581                         }
582                 }
583                 
584                 bool IList.IsReadOnly {
585                         [MonoTODO]
586                         get {
587                                 return false;
588                         }
589                 }
590
591                 object IList.this[int recordIndex] {
592                         [MonoTODO]
593                         get {
594                                 return this[recordIndex];
595                         }
596
597                         [MonoTODO]
598                         set{
599                                 throw new InvalidOperationException();
600                         }\r
601                 }
602
603                 [MonoTODO]
604                 int IList.Add (object value) 
605                 {
606                         throw new NotImplementedException ();
607                 }
608
609                 [MonoTODO]
610                 void IList.Clear () 
611                 {
612                         throw new NotImplementedException ();
613                 }
614
615                 [MonoTODO]
616                 bool IList.Contains (object value) 
617                 {
618                         throw new NotImplementedException ();
619                 }
620
621                 [MonoTODO]
622                 int IList.IndexOf (object value) 
623                 {
624                         throw new NotImplementedException ();
625                 }
626                         
627                 [MonoTODO]
628                 void IList.Insert(int index,object value) 
629                 {
630                         throw new NotImplementedException ();
631                 }
632
633                 [MonoTODO]
634                 void IList.Remove(object value) 
635                 {
636                         throw new NotImplementedException ();
637                 }
638
639                 [MonoTODO]
640                 void IList.RemoveAt(int index) 
641                 {
642                         throw new NotImplementedException ();
643                 }
644
645                 #region IBindingList implementation
646
647                 [MonoTODO]
648                 void IBindingList.AddIndex (PropertyDescriptor property) 
649                 {
650                         throw new NotImplementedException ();
651                 }
652
653                 [MonoTODO]
654                 object IBindingList.AddNew () 
655                 {
656                         throw new NotImplementedException ();
657                 }
658
659                 [MonoTODO]
660                 void IBindingList.ApplySort (PropertyDescriptor property, ListSortDirection direction) 
661                 {
662                         throw new NotImplementedException ();
663                 }
664
665                 [MonoTODO]
666                 int IBindingList.Find (PropertyDescriptor property, object key) 
667                 {
668                         throw new NotImplementedException ();
669                 }
670
671                 [MonoTODO]
672                 void IBindingList.RemoveIndex (PropertyDescriptor property) 
673                 {
674                         throw new NotImplementedException ();
675                 }
676
677                 [MonoTODO]
678                 void IBindingList.RemoveSort () 
679                 {
680                         throw new NotImplementedException ();
681                 }
682                 
683                 bool IBindingList.AllowEdit {
684                         [MonoTODO]
685                         get {
686                                 return AllowEdit;
687                         }
688                 }
689
690                 bool IBindingList.AllowNew {
691                         [MonoTODO]
692                         get {
693                                 return AllowNew;
694                         }
695                 }
696
697                 bool IBindingList.AllowRemove {
698                         [MonoTODO]
699                         get {
700                                 return AllowDelete;
701                         }
702                 }
703
704                 bool IBindingList.IsSorted {
705                         [MonoTODO]
706                         get {
707                                 return isSorted;
708                         }
709                 }
710
711                 ListSortDirection IBindingList.SortDirection {
712                         [MonoTODO]
713                         get {
714                                 // FIXME: 
715                                 return ListSortDirection.Ascending;
716                         }
717                 }
718
719                 PropertyDescriptor IBindingList.SortProperty {
720                         [MonoTODO]
721                         get {
722                                 // FIXME:
723                                 return null;
724                         }
725                 }
726
727                 bool IBindingList.SupportsChangeNotification {
728                         [MonoTODO]
729                         get {
730                                 return false;
731                         }
732                 }
733
734                 bool IBindingList.SupportsSearching {
735                         [MonoTODO]
736                         get {
737                                 return false;
738                         }
739                 }
740
741                 bool IBindingList.SupportsSorting {
742                         [MonoTODO]
743                         get {
744                                 return false;
745                         }
746                 }
747
748                 #endregion // IBindingList implementation
749
750                 private class DataViewEnumerator : IEnumerator 
751                 {
752                         private DataRowView[] rows;
753                         int on = -1;
754
755                         internal DataViewEnumerator (DataRowView[] dataRowViews) 
756                         {
757                                 rows = dataRowViews;
758                         }
759
760                         public object Current {
761                                 get {
762                                         if (on == -1 || on >= rows.Length)
763                                                 throw new InvalidOperationException ();
764                                         return rows[on];
765                                 }
766                         }
767
768                         public bool MoveNext () 
769                         {
770                                 // TODO: how do you determine
771                                 // if a collection has been
772                                 // changed?
773                                 if (on < rows.Length - 1) {
774                                         on++;
775                                         return true;
776                                 }
777
778                                 return false; // EOF
779                         }
780
781                         public void Reset () {
782                                 on = -1;
783                         }
784                 }
785         }
786 }
787