2006-02-17 Chris Toshok <toshok@ximian.com>
[mono.git] / mcs / class / System.Data / System.Data / DataView.cs
1 //\r
2 // System.Data.DataView.cs\r
3 //\r
4 // Author:\r
5 //    Daniel Morgan <danmorg@sc.rr.com>\r
6 //    Tim Coleman (tim@timcoleman.com)\r
7 //    Punit Todi (punits_mailbox@yahoo.com)\r
8 //    Atsushi Enomoto <atsushi@ximian.com>\r
9 //    Konstantin Triger (kostat@mainsoft.com)\r
10 //\r
11 // Copyright (C) Daniel Morgan, 2002, 2003\r
12 // (C) Ximian, Inc 2002\r
13 // Copyright (C) Tim Coleman, 2002-2003         \r
14 \r
15 using System;\r
16 using System.Collections;\r
17 using System.ComponentModel;\r
18 using System.Reflection;\r
19 using System.Data.Common;\r
20 using System.Globalization;\r
21 using Mono.Data.SqlExpressions;\r
22 using System.Text;\r
23 \r
24 namespace System.Data \r
25 {\r
26         /// <summary>\r
27         /// A DataView is used in the binding of data between\r
28         /// a DataTable and Windows Forms or Web Forms allowing\r
29         /// a view of a DataTable for editing, filtering,\r
30         /// navigation, searching, and sorting.\r
31         /// </summary>\r
32         //[Designer]\r
33         [Editor ("Microsoft.VSDesigner.Data.Design.DataSourceEditor, " + Consts.AssemblyMicrosoft_VSDesigner,\r
34                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]\r
35         [DefaultEvent ("PositionChanged")]\r
36         [DefaultProperty ("Table")]\r
37         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataViewDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]\r
38         public class DataView : MarshalByValueComponent, IBindingList, IList, ICollection, IEnumerable, ITypedList, ISupportInitialize\r
39         {\r
40                 internal DataTable dataTable = null;\r
41                 string rowFilter = String.Empty;\r
42                 IExpression rowFilterExpr;\r
43                 string sort = String.Empty;\r
44                 ListSortDirection [] sortOrder = null;\r
45                 PropertyDescriptor sortProperty = null;\r
46                 DataColumn [] sortColumns = null;\r
47                 internal DataViewRowState rowState;\r
48                 internal DataRowView[] rowCache = new DataRowView [0];\r
49 \r
50                 // BeginInit() support\r
51                 bool isInitPhase = false;\r
52                 bool inEndInit = false;\r
53                 DataTable initTable;\r
54                 bool initApplyDefaultSort;\r
55                 string initSort;\r
56                 string initRowFilter;\r
57                 DataViewRowState initRowState;\r
58 \r
59                 // FIXME: what are the default values?\r
60                 bool allowNew = true; \r
61                 bool allowEdit = true;\r
62                 bool allowDelete = true;\r
63                 bool applyDefaultSort = false;\r
64                 bool isSorted = false;\r
65 \r
66                 bool isOpen = false;\r
67 \r
68                 bool useDefaultSort = true;\r
69                 \r
70                 Index _index;\r
71                 internal DataRow _lastAdded = null;\r
72                 \r
73                 private DataViewManager dataViewManager = null;\r
74                 internal static ListChangedEventArgs ListResetEventArgs = new ListChangedEventArgs (ListChangedType.Reset,-1,-1);\r
75 \r
76                 #region Constructors\r
77 \r
78                 public DataView () \r
79                 {\r
80                         rowState = DataViewRowState.CurrentRows;\r
81                         Open ();\r
82                 }\r
83 \r
84                 public DataView (DataTable table)\r
85                         : this (table, (DataViewManager) null)\r
86                 {\r
87                 }\r
88 \r
89                 internal DataView (DataTable table, DataViewManager manager)\r
90                 {\r
91                         dataTable = table;\r
92                         rowState = DataViewRowState.CurrentRows;\r
93                         dataViewManager = manager;\r
94                         Open ();\r
95                 }\r
96 \r
97                 public DataView (DataTable table, string rowFilter,\r
98                         string sort, DataViewRowState rowState)\r
99                         : this (table, null, rowFilter, sort, rowState)\r
100                 {\r
101                 }\r
102 \r
103                 internal DataView (DataTable table, DataViewManager manager,\r
104                         string RowFilter, string Sort, DataViewRowState RowState)\r
105                 {\r
106                         dataTable = table;\r
107                         dataViewManager = manager;\r
108                         rowState = DataViewRowState.CurrentRows;\r
109                         this.RowFilter = RowFilter;\r
110                         this.Sort = Sort;\r
111                         rowState = RowState;\r
112                         Open ();\r
113                 }\r
114                 #endregion // Constructors\r
115 \r
116                 #region PublicProperties\r
117 \r
118                 [DataCategory ("Data")]\r
119                 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows deletes.")]\r
120                 [DefaultValue (true)]\r
121                 public bool AllowDelete {\r
122                         get {\r
123                                 return allowDelete;\r
124                         }\r
125                         set {\r
126                                 allowDelete = value;\r
127                         }\r
128                 }\r
129 \r
130                 [DataCategory ("Data")]\r
131                 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows edits.")]\r
132                 [DefaultValue (true)]\r
133                 public bool AllowEdit {\r
134                         get {\r
135                                 return allowEdit;\r
136                         }\r
137                         set {\r
138                                 allowEdit = value;\r
139                         }\r
140                 }\r
141 \r
142                 [DataCategory ("Data")]\r
143                 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows new rows to be added.")]\r
144                 [DefaultValue (true)]\r
145                 public bool AllowNew {\r
146                         get {\r
147                                 return allowNew;\r
148                         }\r
149                         \r
150                         set {\r
151                                 allowNew = value;\r
152                         }\r
153                 }\r
154 \r
155                 [DataCategory ("Data")]\r
156                 [DataSysDescription ("Indicates whether to use the default sort if the Sort property is not set.")]\r
157                 [DefaultValue (false)]\r
158                 [RefreshProperties (RefreshProperties.All)]\r
159                 public bool ApplyDefaultSort {\r
160                         get { return applyDefaultSort; }\r
161                         set {\r
162                                 if (isInitPhase) {\r
163                                         initApplyDefaultSort = value;\r
164                                         return;\r
165                                 }\r
166                                 if (applyDefaultSort == value)\r
167                                         return;\r
168 \r
169                                 applyDefaultSort = value;\r
170                                 if (applyDefaultSort == true &&\r
171                                         (sort == null || sort == string.Empty))\r
172                                         PopulateDefaultSort ();\r
173                                 if (!inEndInit) {\r
174                                         UpdateIndex (true);\r
175                                         OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));\r
176                                 }\r
177                         }\r
178                 }\r
179                 // get the count of rows in the DataView after RowFilter \r
180                 // and RowStateFilter have been applied\r
181                 [Browsable (false)]\r
182                 [DataSysDescription ("Returns the number of items currently in this view.")]\r
183                 public int Count {\r
184                         [MonoTODO]\r
185                         get {\r
186                                 return rowCache.Length;\r
187                         }\r
188                 }\r
189 \r
190                 [Browsable (false)]\r
191                 [DataSysDescription ("This returns a pointer to back to the DataViewManager that owns this DataSet (if any).")]\r
192                 public DataViewManager DataViewManager {\r
193                         [MonoTODO]\r
194                         get {\r
195                                 return dataViewManager;\r
196                         }\r
197                 }\r
198 \r
199                 // Item indexer\r
200                 // the compiler creates a DefaultMemeberAttribute from\r
201                 // this IndexerNameAttribute\r
202                 [System.Runtime.CompilerServices.IndexerName("Item")]\r
203                 public DataRowView this[int recordIndex] {\r
204                         [MonoTODO]\r
205                         get {\r
206                                 if (recordIndex > rowCache.Length)\r
207                                         throw new IndexOutOfRangeException ("There is no row at " +\r
208                                                                             "position: " + recordIndex + ".");\r
209                                 return rowCache [recordIndex];\r
210                         }\r
211                 }\r
212 \r
213                 [DataCategory ("Data")]\r
214                 [DataSysDescription ("Indicates an expression used to filter the data returned by this DataView.")]\r
215                 [DefaultValue ("")]\r
216                 public virtual string RowFilter {\r
217                         get { return rowFilter; }\r
218                         [MonoTODO]\r
219                         set {\r
220                                 if (value == null)\r
221                                         value = String.Empty;\r
222                                 if (isInitPhase) {\r
223                                         initRowFilter = value;\r
224                                         return;\r
225                                 }\r
226 \r
227                                 CultureInfo info = (Table != null) ? Table.Locale : CultureInfo.CurrentCulture;\r
228                                 if (String.Compare(rowFilter, value, false, info) == 0)\r
229                                         return;\r
230 \r
231                                 if (value == String.Empty) \r
232                                         rowFilterExpr = null;\r
233                                 else {\r
234                                         Parser parser = new Parser ();\r
235                                         rowFilterExpr = parser.Compile (value);\r
236                                 }\r
237                                 rowFilter = value;\r
238                                 if (!inEndInit)\r
239                                         UpdateIndex (true);\r
240                         }\r
241                 }\r
242 \r
243                 [DataCategory ("Data")]\r
244                 [DataSysDescription ("Indicates the versions of data returned by this DataView.")]\r
245                 [DefaultValue (DataViewRowState.CurrentRows)]\r
246                 public DataViewRowState RowStateFilter {\r
247                         get { return rowState; }\r
248                         set {\r
249                                 if (isInitPhase) {\r
250                                         initRowState = value;\r
251                                         return;\r
252                                 }\r
253 \r
254                                 if (value == rowState)\r
255                                         return;\r
256 \r
257                                 rowState = value;\r
258                                 if (!inEndInit) {\r
259                                         UpdateIndex (true);\r
260                                         OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));\r
261                                 }\r
262                         }\r
263                 }\r
264 \r
265                 [DataCategory ("Data")]\r
266                 [DataSysDescription ("Indicates the order in which data is returned by this DataView.")]\r
267                 [DefaultValue ("")]\r
268                 public string Sort {\r
269                         get { \r
270                                 if (useDefaultSort)\r
271                                         return String.Empty;\r
272                                 else\r
273                                         return sort;\r
274                         }\r
275 \r
276                         set {\r
277                                 if (isInitPhase) {\r
278                                         initSort = value;\r
279                                         return;\r
280                                 }\r
281                                 if (value == sort)\r
282                                         return;\r
283 \r
284                                 if ((value == null) || (value.Equals (String.Empty))) { \r
285                                 /* if given value is null useDefaultSort */\r
286                                         useDefaultSort = true;\r
287                                         /* if ApplyDefault sort is true try appling it */\r
288                                         if (ApplyDefaultSort == true)\r
289                                                 PopulateDefaultSort ();\r
290                                 }\r
291                                 else {  \r
292                                         /* else donot useDefaultSort. set it as false */\r
293                                         /* sort is set to value specified */\r
294                                         useDefaultSort = false;\r
295                                         sort = value;\r
296                                         //sortedColumns = SortableColumn.ParseSortString (dataTable, value, true);\r
297                                 }\r
298                                 if (!inEndInit) {\r
299                                         UpdateIndex (true);\r
300                                         OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));\r
301                                 }\r
302                         }\r
303                 }\r
304 \r
305                 [TypeConverter (typeof (DataTableTypeConverter))]\r
306                 [DataCategory ("Data")]\r
307                 [DataSysDescription ("Indicates the table this DataView uses to get data.")]\r
308                 [DefaultValue (null)]\r
309                 [RefreshProperties (RefreshProperties.All)]\r
310                 public DataTable Table {\r
311                         get { return dataTable; }\r
312                         set {\r
313                                 if (isInitPhase) {\r
314                                         initTable = value;\r
315                                         return;\r
316                                 }\r
317 \r
318                                 if (value != null && value.TableName.Equals("")) {\r
319                                         throw new DataException("Cannot bind to DataTable with no name.");\r
320                                 }\r
321 \r
322                                 if (dataTable != null) {\r
323                                         UnregisterEventHandlers ();\r
324                                 }\r
325 \r
326                                 dataTable = value;\r
327 \r
328                                 if (dataTable != null) {\r
329                                         RegisterEventHandlers ();\r
330                                         OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged, 0, 0));\r
331                                         sort = null;\r
332                                         rowFilter = null;\r
333                                         rowFilterExpr = null;\r
334                                         if (!inEndInit) {\r
335                                                 UpdateIndex (true);\r
336                                                 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));\r
337                                         }\r
338                                 }\r
339                         }\r
340                 }\r
341 \r
342                 #endregion // PublicProperties\r
343                 \r
344                 #region PublicMethods\r
345 \r
346                 [MonoTODO]\r
347                 public virtual DataRowView AddNew() \r
348                 {\r
349                         if (!IsOpen)\r
350                                 throw new DataException ("DataView is not open.");\r
351                         if (!AllowNew)\r
352                                 throw new DataException ("Cannot call AddNew on a DataView where AllowNew is false.");\r
353                         \r
354                         if (_lastAdded != null) {\r
355                                 // FIXME : finish last added\r
356                                 CompleteLastAdded(true);\r
357                         }\r
358 \r
359                         _lastAdded = dataTable.NewRow ();\r
360                         UpdateIndex(true);\r
361                         OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1, -1));\r
362                          \r
363                         return this[Count - 1];\r
364                 }\r
365 \r
366                 internal void CompleteLastAdded(bool add)\r
367                 {\r
368                         DataRow dr = _lastAdded;\r
369 \r
370                         if (add) {\r
371                                 try {\r
372                                         dataTable.Rows.Add(_lastAdded);\r
373                                         //OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1, -1));\r
374                                         _lastAdded = null;\r
375                                 }\r
376                                 catch(Exception e) {\r
377                                         _lastAdded = dr;\r
378                                         throw e;\r
379                                 }\r
380                         }\r
381                         else {\r
382                                 _lastAdded.CancelEdit();\r
383                                 _lastAdded = null;\r
384                                 UpdateIndex ();\r
385                                 OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, Count, -1));\r
386                         }\r
387                 }\r
388 \r
389                 [MonoTODO]\r
390                 public void BeginInit() \r
391                 {\r
392                         initTable = Table;\r
393                         initApplyDefaultSort = ApplyDefaultSort;\r
394                         initSort = Sort;\r
395                         initRowFilter = RowFilter;\r
396                         initRowState = RowStateFilter;\r
397 \r
398                         isInitPhase = true;\r
399                 }\r
400 \r
401                 [MonoTODO]\r
402                 public void CopyTo (Array array, int index) \r
403                 {\r
404                         if (index + rowCache.Length > array.Length) {\r
405                                 throw new IndexOutOfRangeException();\r
406                         }\r
407 \r
408                         int row = 0;\r
409                         for (; row < rowCache.Length && row < array.Length; row++) {\r
410                                 array.SetValue (rowCache[row], index + row);\r
411                         }\r
412                 }\r
413 \r
414                 public void Delete(int index) \r
415                 {\r
416                         if (!IsOpen)\r
417                                 throw new DataException ("DataView is not open.");\r
418 \r
419                         if (_lastAdded != null && index == Count) {\r
420                                 CompleteLastAdded(false);\r
421                                 return;\r
422                         }\r
423 \r
424                         if (!AllowDelete)\r
425                                 throw new DataException ("Cannot delete on a DataSource where AllowDelete is false.");\r
426                         \r
427                         if (index > rowCache.Length)\r
428                                 throw new IndexOutOfRangeException ("There is no row at " +\r
429                                                 "position: " + index + ".");\r
430                         DataRowView row = rowCache [index];\r
431                         row.Row.Delete();\r
432                 }\r
433 \r
434 #if NET_2_0\r
435                 [MonoTODO]\r
436                 public virtual bool Equals (DataView dv)\r
437                 {\r
438                         throw new NotImplementedException ();\r
439                 }\r
440 #endif\r
441 \r
442                 [MonoTODO]\r
443                 public void EndInit() \r
444                 {\r
445                         isInitPhase = false;\r
446 \r
447                         inEndInit = true;\r
448 \r
449                         Table = initTable;\r
450                         ApplyDefaultSort = initApplyDefaultSort;\r
451                         Sort = initSort;\r
452                         RowFilter = initRowFilter;\r
453                         RowStateFilter = initRowState;\r
454 \r
455                         inEndInit = false;\r
456 \r
457                         UpdateIndex (true);\r
458                 }\r
459 \r
460                 [MonoTODO]\r
461                 public int Find(object key) \r
462                 {\r
463                         object [] keys = new object[] { key };\r
464                         return Find(keys);\r
465                 }\r
466                 \r
467                 [MonoTODO]\r
468                 public int Find(object[] keys) \r
469                 {\r
470                         if (sort == null || sort == string.Empty) {\r
471                                 throw new ArgumentException ("Find finds a row based on a Sort order, and no Sort order is specified");\r
472                         }\r
473 \r
474                         if (Index == null) {\r
475                                 UpdateIndex(true);\r
476                         }\r
477 \r
478                         int index = -1;                                 \r
479                         try {\r
480                                 index = Index.FindIndex(keys);\r
481                         }\r
482                         catch(FormatException) {\r
483                                 // suppress exception\r
484                         }\r
485                         catch(InvalidCastException) {\r
486                                 // suppress exception\r
487                         }\r
488                         return index;\r
489                 }\r
490                 \r
491                 [MonoTODO]\r
492                 public DataRowView[] FindRows(object key) \r
493                 {\r
494                         return FindRows(new object[] {key});\r
495                 }\r
496 \r
497                 [MonoTODO]\r
498                 public DataRowView[] FindRows(object[] keys) \r
499                 {\r
500                         if (sort == null || sort == string.Empty) {\r
501                                 throw new ArgumentException ("Find finds a row based on a Sort order, and no Sort order is specified");\r
502                         }\r
503 \r
504                         if (Index == null) {\r
505                                 UpdateIndex(true);\r
506                         }\r
507 \r
508                         int[] indexes = Index.FindAllIndexes(keys);\r
509 \r
510                         DataRowView[] rowViewArr = new DataRowView[indexes.Length];                     \r
511                         for (int r = 0; r < indexes.Length; r++) {\r
512                                 rowViewArr[r] = rowCache[indexes[r]];\r
513                         }\r
514                         return rowViewArr;\r
515                 }\r
516 \r
517                 public IEnumerator GetEnumerator() \r
518                 {\r
519                         DataRowView[] dataRowViews = new DataRowView[Count];\r
520                         CopyTo(dataRowViews,0);\r
521                         return dataRowViews.GetEnumerator();\r
522                 }\r
523 \r
524                 #endregion // PublicMethods\r
525                 \r
526                 [DataCategory ("Data")]\r
527                 [DataSysDescription ("Indicates that the data returned by this DataView has somehow changed.")]\r
528                 public event ListChangedEventHandler ListChanged;\r
529 \r
530                 [Browsable (false)]\r
531                 [DataSysDescription ("Indicates whether the view is open.  ")]\r
532                 protected bool IsOpen {\r
533                         get { return isOpen; }\r
534                 }\r
535 \r
536                 internal Index Index\r
537                 {\r
538                         get {\r
539                                 return _index;\r
540                         }\r
541 \r
542                         set {\r
543                                 if (_index != null) {\r
544                                         _index.RemoveRef();\r
545                                         Table.DropIndex(_index);\r
546                                 }\r
547 \r
548                                 _index = value;\r
549 \r
550                                 if (_index != null) {\r
551                                         _index.AddRef();\r
552                                 }\r
553                         }\r
554                 }\r
555 \r
556                 protected void Close ()\r
557                 {\r
558                         if (dataTable != null)\r
559                                 UnregisterEventHandlers ();\r
560                         Index = null;\r
561                         rowCache = new DataRowView [0];\r
562                         isOpen = false;\r
563                 }\r
564 \r
565                 protected override void Dispose (bool disposing)\r
566                 {\r
567                         if (disposing)\r
568                                 Close ();\r
569 \r
570                         base.Dispose (disposing);\r
571                 }\r
572 \r
573                 protected virtual void IndexListChanged (\r
574                         object sender, ListChangedEventArgs e)\r
575                 {\r
576                 }\r
577 \r
578                 [MonoTODO]\r
579                 protected virtual void OnListChanged(ListChangedEventArgs e) \r
580                 {\r
581                         // Yes, under MS.NET, when it is overriden, the \r
582                         // events are not fired (even if it is essential\r
583                         // to internal processing).\r
584                         try {\r
585                         if (ListChanged != null)\r
586                                 ListChanged (this, e);\r
587                         } catch {\r
588                         }\r
589                 }\r
590 \r
591                 internal void ChangedList(ListChangedType listChangedType, int newIndex,int oldIndex)\r
592                 {\r
593                         ListChangedEventArgs e = new ListChangedEventArgs(listChangedType,newIndex,oldIndex);\r
594                         OnListChanged(e);\r
595                 }\r
596 \r
597                 [MonoTODO]\r
598                 protected void Open() \r
599                 {\r
600                         // I wonder if this comment is still valid, but keep\r
601                         // in the meantime.\r
602 \r
603                         // FIXME: create the initial index cache to the DataTable, and \r
604                         //        only refresh the index when the DataTable\r
605                         //        has changes via column, row, or constraint\r
606                         //        changed events. the index cache is generally\r
607                         //        a DataViewRow array that points to the actual\r
608                         //        DataRows in the this DataTable's DataRowCollection;\r
609                         //        this index is really a cache that gets \r
610                         //        created during Open(), gets Updated \r
611                         //        when various properties of this view\r
612                         //        changes, gets Updated when this DataTable's \r
613                         //        row, column, or constraint collections have changed.\r
614                         //        I'm not sure what else.\r
615                         //        The data view will know one of the DataTable's\r
616                         //        collections have changed via one of \r
617                         //        its changed events.\r
618                         //        Otherwise, if getting a/the DataRowView(s),\r
619                         //        Count, or other properties, then just use the\r
620                         //        index cache.\r
621                         //              dataTable.ColumnChanged  += new DataColumnChangeEventHandler(OnColumnChanged);\r
622                         \r
623                         UpdateIndex (true);\r
624                         if (dataTable != null) {\r
625                                 RegisterEventHandlers();\r
626                         }\r
627                         isOpen = true;\r
628                 }\r
629                 \r
630                 private void RegisterEventHandlers()\r
631                 {\r
632                         //dataTable.ColumnChanging += new DataColumnChangeEventHandler(OnColumnChanging);\r
633                         dataTable.ColumnChanged  += new DataColumnChangeEventHandler(OnColumnChanged);\r
634                         dataTable.RowChanged     += new DataRowChangeEventHandler(OnRowChanged);\r
635                         //dataTable.RowDeleting    += new DataRowChangeEventHandler(OnRowDeleting);\r
636                         dataTable.RowDeleted     += new DataRowChangeEventHandler(OnRowDeleted);\r
637                         dataTable.Columns.CollectionChanged += new CollectionChangeEventHandler(ColumnCollectionChanged);\r
638                         dataTable.Constraints.CollectionChanged += new CollectionChangeEventHandler(OnConstraintCollectionChanged);\r
639 \r
640                         dataTable.Rows.ListChanged += new ListChangedEventHandler (OnRowCollectionChanged);\r
641                 }\r
642                 \r
643                 private void OnRowCollectionChanged (object sender, ListChangedEventArgs args)\r
644                 {\r
645                         if (args.ListChangedType == ListChangedType.Reset)\r
646                                 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1 ));\r
647                 }\r
648 \r
649                 private void UnregisterEventHandlers()\r
650                 {\r
651 //                      dataTable.ColumnChanging -= new DataColumnChangeEventHandler(OnColumnChanging);\r
652                         dataTable.ColumnChanged  -= new DataColumnChangeEventHandler(OnColumnChanged);\r
653                         dataTable.RowChanged     -= new DataRowChangeEventHandler(OnRowChanged);\r
654 //                      dataTable.RowDeleting    -= new DataRowChangeEventHandler(OnRowDeleting);\r
655                         dataTable.RowDeleted     -= new DataRowChangeEventHandler(OnRowDeleted);\r
656                         dataTable.Columns.CollectionChanged -= new CollectionChangeEventHandler(ColumnCollectionChanged);\r
657                         dataTable.Constraints.CollectionChanged -= new CollectionChangeEventHandler(OnConstraintCollectionChanged);\r
658 \r
659                         dataTable.Rows.ListChanged -= new ListChangedEventHandler (OnRowCollectionChanged);\r
660                 }\r
661 \r
662                 // These index storing and rowView preservation must be done\r
663                 // before the actual row value is changed; thus we can't use\r
664                 // RowChanging which accepts "already modified" DataRow.\r
665 \r
666                 private void OnColumnChanged(object sender, DataColumnChangeEventArgs args)\r
667                 {       /* not used */\r
668                         //UpdateIndex(true);\r
669                 }\r
670                 \r
671                 private void OnRowChanged(object sender, DataRowChangeEventArgs args)\r
672                 {\r
673                         int oldIndex,newIndex;\r
674                         oldIndex = newIndex = -1;\r
675                         oldIndex = IndexOf (args.Row);\r
676                         UpdateIndex (true);\r
677                         newIndex = IndexOf (args.Row);\r
678 \r
679                         /* ItemAdded */\r
680                         if(args.Action == DataRowAction.Add)\r
681                         {\r
682                                 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemAdded, newIndex, -1));\r
683                         }\r
684                                 \r
685                         /* ItemChanged or ItemMoved */\r
686                         if (args.Action == DataRowAction.Change) {\r
687                                 if (oldIndex == newIndex)\r
688                                         OnListChanged (new ListChangedEventArgs (ListChangedType.ItemChanged, newIndex, -1));\r
689                                 else\r
690                                         OnListChanged (new ListChangedEventArgs (ListChangedType.ItemMoved, newIndex, oldIndex));\r
691                         }\r
692                 }\r
693 \r
694                 private void OnRowDeleted (object sender, DataRowChangeEventArgs args)\r
695                 {\r
696                         /* ItemDeleted */\r
697                         int newIndex;\r
698                         newIndex = IndexOf (args.Row);\r
699                         UpdateIndex (true);\r
700                         OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, newIndex, -1));\r
701                 }\r
702                 \r
703                 protected virtual void ColumnCollectionChanged (object sender, CollectionChangeEventArgs args)\r
704                 {\r
705                         // UpdateIndex() is not invoked here (even if the sort\r
706                         // column is being removed).\r
707 \r
708                         /* PropertyDescriptor Add */\r
709                         if (args.Action == CollectionChangeAction.Add)\r
710                                 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorAdded,0,0));\r
711                         /* PropertyDescriptor Removed */\r
712                         if (args.Action == CollectionChangeAction.Remove)\r
713                                 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorDeleted,0,0));\r
714                         /* FIXME: PropertyDescriptor Changed ???*/\r
715                         if (args.Action == CollectionChangeAction.Refresh)\r
716                                 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged,0,0));\r
717                 }\r
718                 private void OnConstraintCollectionChanged(object sender, CollectionChangeEventArgs args)\r
719                 {\r
720                         //      The Sort variable is set to the UniqueConstraint column.\r
721                         //  if ApplyDefault Sort is true and Sort is null or is not set Explicitly\r
722                         \r
723                         // FIXME: The interal cache may change as result of change in Constraint collection\r
724                         // one such scenerio is taken care.\r
725                         // There may be more. I dont know what else can be done.\r
726                         /* useDefaultSort is set to false when Sort is set explicitly */\r
727                         if (args.Action == CollectionChangeAction.Add && args.Element is UniqueConstraint) {\r
728                                 if (ApplyDefaultSort == true && useDefaultSort == true)\r
729                                         PopulateDefaultSort ((UniqueConstraint) args.Element);\r
730                         }\r
731                         // UpdateIndex() is not invoked here.\r
732                 }\r
733 \r
734                 // internal use by Mono\r
735                 protected void Reset() \r
736                 {\r
737                         // TODO: what really happens?\r
738                         Close ();\r
739                         rowCache = new DataRowView [0];\r
740                         Open ();\r
741                         OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1 ));\r
742                 }\r
743 \r
744 #if NET_2_0\r
745                 [MonoTODO]\r
746                 public DataTable ToTable ()\r
747                 {\r
748                         throw new NotImplementedException ();\r
749                 }\r
750 \r
751                 [MonoTODO]\r
752                 public DataTable ToTable (bool isDistinct, string[] columnNames)\r
753                 {\r
754                         throw new NotImplementedException ();\r
755                 }\r
756 #endif\r
757                 protected void UpdateIndex() {\r
758                         UpdateIndex(false);\r
759                 }\r
760 \r
761                 // This is method is internal to \r
762                 // the Mono implementation of DataView; it\r
763                 // is not to be used from your code.\r
764                 //\r
765                 // Update the DataRowView array which is an index cache\r
766                 // into the DataTable's DataRowCollection.\r
767                 //\r
768                 // I assume this is what UpdateIndex is used for\r
769                 protected virtual void UpdateIndex(bool force) \r
770                 {\r
771                         if (Table == null) {\r
772                                 // FIXME\r
773                                 return;\r
774                         }\r
775 \r
776                         if (Index == null || force) {\r
777                                 sortColumns = DataTable.ParseSortString(Table, Sort, out sortOrder, false);\r
778                                 Index = dataTable.GetIndex(sortColumns,sortOrder,RowStateFilter,FilterExpression,true);\r
779                         }\r
780                         else {\r
781                                 Index.Key.RowStateFilter = RowStateFilter;\r
782                                 Index.Reset();\r
783                         }\r
784 \r
785                         int[] records = Index.GetAll();\r
786 \r
787                         if (records != null) {\r
788                                 InitDataRowViewArray(records,Index.Size);\r
789                         }\r
790                         else {\r
791                                 rowCache = new DataRowView[0];\r
792                         }\r
793                 }\r
794 \r
795                 internal virtual IExpression FilterExpression\r
796                 {\r
797                         get {\r
798                                 return rowFilterExpr;\r
799                         }\r
800                 }\r
801 \r
802                 private void InitDataRowViewArray(int[] records,int size) \r
803                 {\r
804                         if (_lastAdded != null) {\r
805                                 rowCache = new DataRowView[size + 1];   \r
806                         }\r
807                         else {\r
808                                 rowCache = new DataRowView[size];                       \r
809                         }\r
810 \r
811                         for (int r = 0; r < size; r++) {\r
812                                 rowCache[r] = new DataRowView (this, Table.RecordCache[records[r]],r);\r
813                         }\r
814 \r
815                         if(_lastAdded != null) {\r
816                                 rowCache[size] = new DataRowView(this,_lastAdded,size);\r
817                         }\r
818                 }\r
819 \r
820                 [MonoTODO]\r
821                 PropertyDescriptorCollection ITypedList.GetItemProperties (PropertyDescriptor[] listAccessors) \r
822                 {\r
823                         // FIXME: use listAccessors somehow\r
824                         DataColumnPropertyDescriptor [] descriptors = \r
825                                 new DataColumnPropertyDescriptor [dataTable.Columns.Count];\r
826 \r
827                         DataColumnPropertyDescriptor descriptor;\r
828                         DataColumn dataColumn;\r
829                         for (int col = 0; col < dataTable.Columns.Count; col ++) {\r
830                                 dataColumn = dataTable.Columns[col];\r
831                                 descriptor = new DataColumnPropertyDescriptor(\r
832                                         dataColumn.ColumnName, col, null);\r
833                                 descriptor.SetComponentType (typeof (System.Data.DataRowView));\r
834                                 descriptor.SetPropertyType (dataColumn.DataType);\r
835                                 descriptor.SetReadOnly (dataColumn.ReadOnly);\r
836                                 descriptors [col] = descriptor;\r
837                         }\r
838                         return new PropertyDescriptorCollection (descriptors);\r
839                 }\r
840 \r
841                 \r
842                 string ITypedList.GetListName (PropertyDescriptor[] listAccessors) \r
843                 {\r
844                         if (dataTable != null) {                                \r
845                                 return dataTable.TableName;\r
846                         }\r
847                         return "";\r
848                 }\r
849 \r
850                 bool ICollection.IsSynchronized { \r
851                         [MonoTODO]\r
852                         get {\r
853                                 return false;\r
854                         } \r
855                 }\r
856 \r
857                 object ICollection.SyncRoot { \r
858                         [MonoTODO]\r
859                         get {\r
860                                 // FIXME:\r
861                                 return this;\r
862                         }\r
863                 }\r
864 \r
865                 bool IList.IsFixedSize {\r
866                         [MonoTODO]\r
867                         get {\r
868                                 return false;\r
869                         }\r
870                 }\r
871                 \r
872                 bool IList.IsReadOnly {\r
873                         [MonoTODO]\r
874                         get {\r
875                                 return false;\r
876                         }\r
877                 }\r
878 \r
879                 object IList.this[int recordIndex] {\r
880                         [MonoTODO]\r
881                         get {\r
882                                 return this[recordIndex];\r
883                         }\r
884 \r
885                         [MonoTODO]\r
886                         set{\r
887                                 throw new InvalidOperationException();\r
888                         }\r
889                 }\r
890 \r
891                 int IList.Add (object value) \r
892                 {\r
893                         throw new ArgumentException ("Cannot add external objects to this list.");\r
894                 }\r
895 \r
896                 void IList.Clear () \r
897                 {\r
898                         throw new ArgumentException ("Cannot clear this list.");\r
899                 }\r
900 \r
901                 bool IList.Contains (object value) \r
902                 {\r
903                         DataRowView drv = value as DataRowView;\r
904                         if (drv == null)\r
905                                 return false;\r
906 \r
907                         return drv.DataView == this;\r
908                 }\r
909 \r
910                 int IList.IndexOf (object value) \r
911                 {\r
912                         DataRowView drv = value as DataRowView;\r
913                         if (drv != null && drv.DataView == this) {\r
914                                 return drv.Index;\r
915                         }\r
916 \r
917                         return -1;\r
918                 }\r
919 \r
920                 void IList.Insert(int index,object value) \r
921                 {\r
922                         throw new ArgumentException ("Cannot insert external objects to this list.");\r
923                 }\r
924 \r
925                 void IList.Remove(object value) \r
926                 {\r
927                         DataRowView drv = value as DataRowView;\r
928                         if (drv != null && drv.DataView == this) {\r
929                                 ((IList)this).RemoveAt(drv.Index);\r
930                         }\r
931 \r
932                         throw new ArgumentException ("Cannot remove external objects to this list.");\r
933                 }\r
934 \r
935                 void IList.RemoveAt(int index) \r
936                 {\r
937                         Delete(index);\r
938                 }\r
939 \r
940                 #region IBindingList implementation\r
941 \r
942                 [MonoTODO]\r
943                 void IBindingList.AddIndex (PropertyDescriptor property) \r
944                 {\r
945                         throw new NotImplementedException ();\r
946                 }\r
947 \r
948                 object IBindingList.AddNew () \r
949                 {\r
950                         return this.AddNew ();\r
951                 }\r
952 \r
953                 void IBindingList.ApplySort (PropertyDescriptor property, ListSortDirection direction) \r
954                 {\r
955                         if (! (property is DataColumnPropertyDescriptor))\r
956                                 throw new ArgumentException ("Dataview accepts only DataColumnPropertyDescriptors", "property");\r
957                         sortProperty = property;\r
958                         string sort = String.Format ("[{0}]" , property.Name);\r
959                         if (direction == ListSortDirection.Descending)\r
960                                 sort += " DESC";\r
961                         this.Sort = sort;\r
962                 }\r
963 \r
964                 int IBindingList.Find (PropertyDescriptor property, object key) \r
965                 {\r
966                         DataColumn dc = Table.Columns [property.Name];\r
967                         Index index = Table.FindIndex (new DataColumn [] { dc }, sortOrder, RowStateFilter, FilterExpression);\r
968                         if (index == null)\r
969                                 index = new Index(new Key (Table, new DataColumn [] { dc }, sortOrder, RowStateFilter, FilterExpression));\r
970                         return index.FindIndex (new object [] {key});\r
971                 }\r
972 \r
973                 [MonoTODO]\r
974                 void IBindingList.RemoveIndex (PropertyDescriptor property) \r
975                 {\r
976                         throw new NotImplementedException ();\r
977                 }\r
978 \r
979                 void IBindingList.RemoveSort () \r
980                 {\r
981                         sortProperty = null;\r
982                         this.Sort = String.Empty;\r
983                 }\r
984                 \r
985                 bool IBindingList.AllowEdit {\r
986                         get {\r
987                                 return AllowEdit;\r
988                         }\r
989                 }\r
990 \r
991                 bool IBindingList.AllowNew {\r
992                         get {\r
993                                 return AllowNew;\r
994                         }\r
995                 }\r
996 \r
997                 bool IBindingList.AllowRemove {\r
998                         [MonoTODO]\r
999                         get {\r
1000                                 return AllowDelete;\r
1001                         }\r
1002                 }\r
1003 \r
1004                 bool IBindingList.IsSorted {\r
1005                         get {\r
1006                                 return Sort != null && Sort != String.Empty;\r
1007                         }\r
1008                 }\r
1009 \r
1010                 ListSortDirection IBindingList.SortDirection {\r
1011                         get {\r
1012                                 if (sortOrder != null && sortOrder.Length > 0)\r
1013                                         return sortOrder [0];\r
1014                                 return ListSortDirection.Ascending;\r
1015                         }\r
1016                 }\r
1017 \r
1018                 PropertyDescriptor IBindingList.SortProperty {\r
1019                         get {\r
1020                                 if (sortProperty == null && sortColumns != null && sortColumns.Length > 0) {\r
1021                                         // return property from Sort String\r
1022                                         PropertyDescriptorCollection properties = ( (ITypedList) this).GetItemProperties (null);\r
1023                                         return properties.Find ( sortColumns [0].ColumnName, false);\r
1024                                 }\r
1025                                 return sortProperty;\r
1026                         }\r
1027                 }\r
1028 \r
1029                 bool IBindingList.SupportsChangeNotification {\r
1030                         get {\r
1031                                 return true;\r
1032                         }\r
1033                 }\r
1034 \r
1035                 bool IBindingList.SupportsSearching {\r
1036                         get {\r
1037                                 return true;\r
1038                         }\r
1039                 }\r
1040 \r
1041                 bool IBindingList.SupportsSorting {\r
1042                         get {\r
1043                                 return true;\r
1044                         }\r
1045                 }\r
1046 \r
1047                 #endregion // IBindingList implementation\r
1048                 private int IndexOf(DataRow dr)\r
1049                 {\r
1050                         for (int i=0; i < rowCache.Length; i++)\r
1051                                 if (dr.Equals (rowCache [i].Row))\r
1052                                 return i;\r
1053                         return -1;\r
1054                 }\r
1055                 \r
1056                 private void PopulateDefaultSort () {\r
1057                         sort = "";\r
1058                         foreach (Constraint c in dataTable.Constraints) {\r
1059                                 if (c is UniqueConstraint) {\r
1060                                         PopulateDefaultSort ((UniqueConstraint) c);\r
1061                                         break;\r
1062                                 }\r
1063                         }\r
1064                 }\r
1065 \r
1066                 private void PopulateDefaultSort (UniqueConstraint uc) {\r
1067                         if (isInitPhase)\r
1068                                 return;\r
1069 \r
1070                         DataColumn[] columns = uc.Columns;\r
1071                         if (columns.Length == 0) {\r
1072                                 sort = String.Empty;\r
1073                                 return;\r
1074                         }\r
1075 \r
1076                         StringBuilder builder = new StringBuilder();\r
1077                         builder.Append(columns[0].ColumnName);\r
1078                         for (int i = 1; i<columns.Length; i++) {\r
1079                                 builder.Append(", ");\r
1080                                 builder.Append(columns[i].ColumnName);\r
1081                         }\r
1082                         sort = builder.ToString();\r
1083                 }\r
1084                 \r
1085                 internal DataView CreateChildView (DataRelation relation, int index)\r
1086                 {\r
1087                         if (relation == null || relation.ParentTable != Table) {\r
1088                                 throw new ArgumentException("The relation is not parented to the table to which this DataView points.");\r
1089                         }\r
1090 \r
1091                         int record = GetRecord(index);\r
1092                         object[] keyValues = new object[relation.ParentColumns.Length];\r
1093                         for(int i=0; i < relation.ParentColumns.Length; i++)\r
1094                                 keyValues [i] = relation.ParentColumns [i][record];\r
1095 \r
1096                         return new RelatedDataView(relation.ChildColumns,keyValues);\r
1097                 }\r
1098 \r
1099                 private int GetRecord(int index) {\r
1100                         if (index < 0 || index >= Count)\r
1101                                 throw new IndexOutOfRangeException(String.Format("There is no row at position {0}.", index));\r
1102 \r
1103                         return(index == Index.Size) ?\r
1104                                 _lastAdded.IndexFromVersion(DataRowVersion.Default) :\r
1105                                 Index.IndexToRecord(index);\r
1106                 }\r
1107 \r
1108                 internal DataRowVersion GetRowVersion(int index) {\r
1109                         int record = GetRecord(index);\r
1110                         return Table.RecordCache[record].VersionFromIndex(record);\r
1111                 }\r
1112         }\r
1113 }\r