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