2010-07-25 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / System.Data / System.Data / DataColumnCollection.cs
1 //
2 // System.Data.DataColumnCollection.cs
3 //
4 // Author:
5 //   Christopher Podurgiel (cpodurgiel@msn.com)
6 //   Stuart Caborn      <stuart.caborn@virgin.net>
7 //   Tim Coleman (tim@timcoleman.com)
8 //
9 // (C) Chris Podurgiel
10 // Copyright (C) Tim Coleman, 2002
11 // Copyright (C) Daniel Morgan, 2003
12 //
13
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 //
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 //
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.Text;
39 using System.Collections;
40 using System.ComponentModel;
41
42 namespace System.Data {
43
44         internal class Doublet
45         {
46                 public Doublet (int count, string columnname)
47                 {
48                         this.count = count;
49                         this.columnNames.Add (columnname);
50                 }
51                 // Number of case insensitive column name
52                 public int count;
53                 // Array of exact column names
54                 public ArrayList columnNames = new ArrayList ();
55         }
56
57         [Editor ("Microsoft.VSDesigner.Data.Design.ColumnsCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
58                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
59 #if !NET_2_0
60         [Serializable]
61 #endif
62         [DefaultEvent ("CollectionChanged")]
63         public
64 #if NET_2_0
65         sealed
66 #endif
67         class DataColumnCollection : InternalDataCollectionBase {
68                 //This hashtable maps between unique case insensetive column name to a doublet containing column ref and column count
69 #if NET_2_0
70                 private Hashtable columnNameCount = new Hashtable (StringComparer.OrdinalIgnoreCase);
71 #else
72                 private Hashtable columnNameCount = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
73 #endif
74                 //This hashtable maps between column name to DataColumn object.
75                 private Hashtable columnFromName = new Hashtable ();
76                 //This ArrayList contains the auto-increment columns names
77                 private ArrayList autoIncrement = new ArrayList ();
78                 //This holds the next index to use for default column name.
79                 private int defaultColumnIndex = 1;
80                 //table should be the DataTable this DataColumnCollection belongs to.
81                 private DataTable parentTable = null;
82                 // Keep reference to most recent columns passed to AddRange()
83                 // so that they can be added when EndInit() is called.
84                 DataColumn [] _mostRecentColumns = null;
85
86                 static readonly string ColumnPrefix = "Column";
87
88                 // Internal Constructor.  This Class can only be created from other classes in this assembly.
89                 internal DataColumnCollection (DataTable table)
90                 {
91                         parentTable = table;
92                 }
93
94                 /// <summary>
95                 /// Gets the DataColumn from the collection at the specified index.
96                 /// </summary>
97                 public
98 #if !NET_2_0
99                 virtual
100 #endif
101                 DataColumn this [int index] {
102                         get {
103                                 if (index < 0 || index >= base.List.Count)
104                                         throw new IndexOutOfRangeException ("Cannot find column " + index + ".");
105                                 return (DataColumn) base.List [index];
106                         }
107                 }
108
109                 /// <summary>
110                 /// Gets the DataColumn from the collection with the specified name.
111                 /// </summary>
112                 public
113 #if !NET_2_0
114                 virtual
115 #endif
116                 DataColumn this [string name] {
117                         get {
118 #if NET_2_0
119                                 if (name == null)
120                                         throw new ArgumentNullException ("name");
121 #endif
122
123                                 DataColumn dc = columnFromName [name] as DataColumn;
124                                 if (dc != null)
125                                         return dc;
126
127                                 int tmp = IndexOf (name, true);
128                                 return tmp == -1 ? null : (DataColumn) base.List [tmp];
129                         }
130                 }
131
132                 /// <summary>
133                 /// Gets a list of the DataColumnCollection items.
134                 /// </summary>
135                 protected override ArrayList List {
136                         get { return base.List; }
137                 }
138
139                 internal ArrayList AutoIncrmentColumns {
140                         get { return autoIncrement; }
141                 }
142
143                 //Add Logic
144                 //
145                 //Changing Event
146                 //DefaultValue set and AutoInc set check
147                 //?Validate Expression??
148                 //Name check and creation
149                 //Set Table
150                 //Check Unique if true then add a unique constraint
151                 //?Notify Rows of new column ?
152                 //Add to collection
153                 //Changed Event
154
155                 /// <summary>
156                 /// Creates and adds a DataColumn object to the DataColumnCollection.
157                 /// </summary>
158                 /// <returns></returns>
159                 public
160 #if !NET_2_0
161                 virtual
162 #endif
163                 DataColumn Add ()
164                 {
165                         DataColumn column = new DataColumn (null);
166                         Add (column);
167                         return column;
168                 }
169
170 #if NET_2_0
171                 public void CopyTo (DataColumn [] array, int index)
172                 {
173                         CopyTo ((Array) array, index);
174                 }
175 #endif
176
177                 internal void RegisterName (string name, DataColumn column)
178                 {
179                         try {
180                                 columnFromName.Add (name, column);
181                         } catch (ArgumentException) {
182                                 throw new DuplicateNameException ("A DataColumn named '" + name + "' already belongs to this DataTable.");
183                         }
184
185                         // Get existing doublet
186                         Doublet d = (Doublet) columnNameCount [name];
187                         if (d != null) {
188                                 // Add reference count
189                                 d.count++;
190                                 // Add a new name
191                                 d.columnNames.Add (name);
192                         } else {
193                                 // no existing doublet
194                                 // create one
195                                 d = new Doublet (1, name);
196                                 columnNameCount [name] = d;
197                         }
198
199 #if NET_2_0
200                         if (name.Length <= ColumnPrefix.Length || !name.StartsWith (ColumnPrefix, StringComparison.Ordinal))
201                                 return;
202 #else
203                         if (name.Length <= ColumnPrefix.Length || !name.StartsWith (ColumnPrefix))
204                                 return;
205 #endif
206
207                         if (name == MakeName (defaultColumnIndex + 1)) {
208                                 do {
209                                         defaultColumnIndex++;
210                                 } while (Contains (MakeName (defaultColumnIndex + 1)));
211                         }
212                 }
213
214                 internal void UnregisterName (string name)
215                 {
216                         if (columnFromName.Contains (name))
217                                 columnFromName.Remove (name);
218
219                         // Get the existing doublet
220                         Doublet d = (Doublet) columnNameCount [name];
221                         if (d != null) {
222                                 // decrease reference count
223                                 d.count--;
224                                 d.columnNames.Remove (name);
225                                 // remove doublet if no more references
226                                 if (d.count == 0)
227                                         columnNameCount.Remove (name);
228                         }
229
230                         if (name.StartsWith(ColumnPrefix) && name == MakeName(defaultColumnIndex - 1)) {
231                                 do {
232                                         defaultColumnIndex--;
233                                 } while (!Contains (MakeName (defaultColumnIndex - 1)) && defaultColumnIndex > 1);
234                         }
235                 }
236
237                 private string GetNextDefaultColumnName ()
238                 {
239                         string defColumnName = MakeName (defaultColumnIndex);
240                         for (int index = defaultColumnIndex + 1; Contains (defColumnName); ++index) {
241                                 defColumnName = MakeName (index);
242                                 defaultColumnIndex++;
243                         }
244                         defaultColumnIndex++;
245                         return defColumnName;
246                 }
247
248                 static readonly string[] TenColumns = { "Column0", "Column1", "Column2", "Column3", "Column4", "Column5", "Column6", "Column7", "Column8", "Column9" };
249
250                 static string MakeName (int index)
251                 {
252                         if (index < 10)
253                                 return TenColumns [index];
254                         return String.Concat (ColumnPrefix, index.ToString());
255                 }
256
257                 /// <summary>
258                 /// Creates and adds the specified DataColumn object to the DataColumnCollection.
259                 /// </summary>
260                 /// <param name="column">The DataColumn to add.</param>
261                 public void Add (DataColumn column)
262                 {
263                         if (column == null)
264                                 throw new ArgumentNullException ("column", "'column' argument cannot be null.");
265
266 #if !NET_2_0
267                         /* in 1.1, they must do this here, as the
268                          * setting of ColumnName below causes an event
269                          * to be raised */
270                         column.PropertyChanged += new PropertyChangedEventHandler (ColumnPropertyChanged);
271 #endif
272
273                         if (column.ColumnName.Length == 0) {
274                                 column.ColumnName = GetNextDefaultColumnName ();
275                         }
276
277 //                      if (Contains(column.ColumnName))
278 //                              throw new DuplicateNameException("A DataColumn named '" + column.ColumnName + "' already belongs to this DataTable.");
279
280                         if (column.Table != null)
281                                 throw new ArgumentException ("Column '" + column.ColumnName + "' already belongs to this or another DataTable.");
282
283                         column.SetTable (parentTable);
284                         RegisterName (column.ColumnName, column);
285                         int ordinal = base.List.Add (column);
286
287 #if NET_2_0
288                         column.Ordinal = ordinal;
289 #else
290                         column.SetOrdinal (ordinal);
291 #endif
292
293                         // Check if the Column Expression is ok
294                         if (column.CompiledExpression != null)
295                                 if (parentTable.Rows.Count == 0)
296                                         column.CompiledExpression.Eval (parentTable.NewRow());
297                                 else
298                                         column.CompiledExpression.Eval (parentTable.Rows[0]);
299
300                         // if table already has rows we need to allocate space
301                         // in the column data container
302                         if (parentTable.Rows.Count > 0)
303                                 column.DataContainer.Capacity = parentTable.RecordCache.CurrentCapacity;
304
305                         if (column.AutoIncrement) {
306                                 DataRowCollection rows = column.Table.Rows;
307                                 for (int i = 0; i < rows.Count; i++)
308                                         rows [i] [ordinal] = column.AutoIncrementValue ();
309                         }
310
311                         if (column.AutoIncrement)
312                                 autoIncrement.Add (column);
313
314 #if NET_2_0
315                         column.PropertyChanged += new PropertyChangedEventHandler (ColumnPropertyChanged);
316 #endif
317
318                         OnCollectionChanged (new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
319                 }
320
321                 /// <summary>
322                 /// Creates and adds a DataColumn object with the specified name to the DataColumnCollection.
323                 /// </summary>
324                 /// <param name="columnName">The name of the column.</param>
325                 /// <returns>The newly created DataColumn.</returns>
326                 public
327 #if !NET_2_0
328                 virtual
329 #endif
330                 DataColumn Add (string columnName)
331                 {
332                         DataColumn column = new DataColumn (columnName);
333                         Add (column);
334                         return column;
335                 }
336
337                 /// <summary>
338                 /// Creates and adds a DataColumn object with the specified name and type to the DataColumnCollection.
339                 /// </summary>
340                 /// <param name="columnName">The ColumnName to use when cretaing the column.</param>
341                 /// <param name="type">The DataType of the new column.</param>
342                 /// <returns>The newly created DataColumn.</returns>
343                 public
344 #if !NET_2_0
345                 virtual
346 #endif
347                 DataColumn Add (string columnName, Type type)
348                 {
349                         if (columnName == null || columnName == "")
350                                 columnName = GetNextDefaultColumnName ();
351
352                         DataColumn column = new DataColumn (columnName, type);
353                         Add (column);
354                         return column;
355                 }
356
357                 /// <summary>
358                 /// Creates and adds a DataColumn object with the specified name, type, and expression to the DataColumnCollection.
359                 /// </summary>
360                 /// <param name="columnName">The name to use when creating the column.</param>
361                 /// <param name="type">The DataType of the new column.</param>
362                 /// <param name="expression">The expression to assign to the Expression property.</param>
363                 /// <returns>The newly created DataColumn.</returns>
364                 public
365 #if !NET_2_0
366                 virtual
367 #endif
368                 DataColumn Add (string columnName, Type type, string expression)
369                 {
370                         if (columnName == null || columnName == "")
371                                 columnName = GetNextDefaultColumnName ();
372
373                         DataColumn column = new DataColumn (columnName, type, expression);
374                         Add (column);
375                         return column;
376                 }
377
378                 /// <summary>
379                 /// Copies the elements of the specified DataColumn array to the end of the collection.
380                 /// </summary>
381                 /// <param name="columns">The array of DataColumn objects to add to the collection.</param>
382                 public void AddRange (DataColumn [] columns)
383                 {
384                         if (parentTable.InitInProgress){
385                                 _mostRecentColumns = columns;
386                                 return;
387                         }
388
389                         if (columns == null)
390                                 return;
391
392                         foreach (DataColumn column in columns){
393                                 if (column == null)
394                                         continue;
395                                 Add(column);
396                         }
397                 }
398
399                 private string GetColumnDependency (DataColumn column)
400                 {
401
402                         foreach (DataRelation rel in parentTable.ParentRelations)
403                                 if (Array.IndexOf (rel.ChildColumns, column) != -1)
404                                         return String.Format (" child key for relationship {0}.", rel.RelationName);
405                         foreach (DataRelation rel in parentTable.ChildRelations)
406                                 if (Array.IndexOf (rel.ParentColumns, column) != -1)
407                                         return String.Format (" parent key for relationship {0}.", rel.RelationName);
408
409                         foreach (Constraint c in parentTable.Constraints)
410                                 if (c.IsColumnContained (column))
411                                         return String.Format (" constraint {0} on the table {1}.",
412                                                         c.ConstraintName, parentTable);
413
414
415                         // check if the foreign-key constraint on any table in the dataset refers to this column.
416                         // though a forignkeyconstraint automatically creates a uniquecontrainton the parent
417                         // table and would fail above, but we still need to check, as it is legal to manually remove
418                         // the constraint on the parent table.
419                         if (parentTable.DataSet != null)
420                                 foreach (DataTable table in parentTable.DataSet.Tables)
421                                         foreach (Constraint c in table.Constraints)
422                                                 if (c is ForeignKeyConstraint && c.IsColumnContained(column))
423                                                         return String.Format (
424                                                                 " constraint {0} on the table {1}.", c.ConstraintName, table.TableName);
425
426                         foreach (DataColumn col in this)
427                                 if (col.CompiledExpression != null && col.CompiledExpression.DependsOn (column))
428                                         return  col.Expression;
429                         return String.Empty;
430                 }
431
432                 /// <summary>
433                 /// Checks whether a given column can be removed from the collection.
434                 /// </summary>
435                 /// <param name="column">A DataColumn in the collection.</param>
436                 /// <returns>true if the column can be removed; otherwise, false.</returns>
437                 public bool CanRemove (DataColumn column)
438                 {
439                         if (column == null || column.Table != parentTable || GetColumnDependency (column) != String.Empty)
440                                 return false;
441                         return true;
442                 }
443
444                 /// <summary>
445                 /// Clears the collection of any columns.
446                 /// </summary>
447                 public void Clear ()
448                 {
449                         CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this);
450
451                         // its not necessary to check if each column in the collection can removed.
452                         // Can simply check, if there are any constraints/relations related to the table,
453                         // in which case, throw an exception.
454                         // Also, shudnt check for expression columns since all the columns in the table
455                         // are being removed.
456                         if (parentTable.Constraints.Count != 0 ||
457                             parentTable.ParentRelations.Count != 0 ||
458                             parentTable.ChildRelations.Count != 0)
459                                 foreach (DataColumn col in this) {
460                                         string s = GetColumnDependency (col);
461                                         if (s != String.Empty)
462                                                 throw new ArgumentException ("Cannot remove this column, because it is part of the" + s);
463                                 }
464
465                         if (parentTable.DataSet != null)
466                                 foreach (DataTable table in parentTable.DataSet.Tables)
467                                         foreach (Constraint c in table.Constraints) {
468                                                 if (!(c is ForeignKeyConstraint) ||
469                                                     ((ForeignKeyConstraint) c).RelatedTable != parentTable)
470                                                         continue;
471                                                 throw new ArgumentException (
472                                                         String.Format (
473                                                                 "Cannot remove this column, because it is part of the constraint {0} on the table {1}",
474                                                                 c.ConstraintName, table.TableName));
475                                         }
476
477                         foreach (DataColumn col in this)
478                                 col.ResetColumnInfo ();
479
480                         columnFromName.Clear ();
481                         autoIncrement.Clear ();
482                         columnNameCount.Clear ();
483                         base.List.Clear ();
484                         defaultColumnIndex = 1;
485                         OnCollectionChanged (e);
486                 }
487
488                 /// <summary>
489                 /// Checks whether the collection contains a column with the specified name.
490                 /// </summary>
491                 /// <param name="name">The ColumnName of the column to check for.</param>
492                 /// <returns>true if a column exists with this name; otherwise, false.</returns>
493                 public bool Contains (string name)
494                 {
495                         if (columnFromName.Contains (name))
496                                 return true;
497
498                         return (IndexOf (name, false) != -1);
499                 }
500
501                 /// <summary>
502                 /// Gets the index of a column specified by name.
503                 /// </summary>
504                 /// <param name="column">The name of the column to return.</param>
505                 /// <returns>The index of the column specified by column if it is found; otherwise, -1.</returns>
506                 public
507 #if !NET_2_0
508                 virtual
509 #endif
510                 int IndexOf (DataColumn column)
511                 {
512                         if (column == null)
513                                 return -1;
514                         return base.List.IndexOf (column);
515                 }
516
517                 /// <summary>
518                 /// Gets the index of the column with the given name (the name is not case sensitive).
519                 /// </summary>
520                 /// <param name="columnName">The name of the column to find.</param>
521                 /// <returns>The zero-based index of the column with the specified name, or -1 if the column doesn't exist in the collection.</returns>
522                 public int IndexOf (string columnName)
523                 {
524                         if (columnName == null)
525                                 return -1;
526                         DataColumn dc = columnFromName [columnName] as DataColumn;
527
528                         if (dc != null)
529                                 return IndexOf (dc);
530
531                         return IndexOf (columnName, false);
532                 }
533
534                 /// <summary>
535                 /// Raises the OnCollectionChanged event.
536                 /// </summary>
537                 /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
538 #if !NET_2_0
539                 protected virtual
540 #else
541                 internal
542 #endif
543                 void OnCollectionChanged (CollectionChangeEventArgs ccevent)
544                 {
545                         parentTable.ResetPropertyDescriptorsCache ();
546                         if (CollectionChanged != null)
547                                 CollectionChanged (this, ccevent);
548                 }
549
550                 /// <summary>
551                 /// Raises the OnCollectionChanging event.
552                 /// </summary>
553                 /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
554 #if !NET_2_0
555                 protected internal virtual
556 #else
557                 internal
558 #endif
559                 void OnCollectionChanging (CollectionChangeEventArgs ccevent)
560                 {
561                         if (CollectionChanged != null) {
562                                 //FIXME: this is not right
563                                 //CollectionChanged(this, ccevent);
564                                 throw new NotImplementedException();
565                         }
566                 }
567
568                 /// <summary>
569                 /// Removes the specified DataColumn object from the collection.
570                 /// </summary>
571                 /// <param name="column">The DataColumn to remove.</param>
572                 public void Remove (DataColumn column)
573                 {
574                         if (column == null)
575                                 throw new ArgumentNullException ("column", "'column' argument cannot be null.");
576
577                         if (!Contains (column.ColumnName))
578                                 throw new ArgumentException ("Cannot remove a column that doesn't belong to this table.");
579
580                         string dependency = GetColumnDependency (column);
581                         if (dependency != String.Empty)
582                                 throw new ArgumentException ("Cannot remove this column, because it is part of " + dependency);
583
584                         CollectionChangeEventArgs e = new CollectionChangeEventArgs (CollectionChangeAction.Remove, column);
585
586                         int ordinal = column.Ordinal;
587                         UnregisterName (column.ColumnName);
588                         base.List.Remove (column);
589
590                         // Reset column info
591                         column.ResetColumnInfo ();
592
593                         //Update the ordinals
594                         for( int i = ordinal ; i < this.Count ; i ++ )
595 #if NET_2_0
596                                 this[i].Ordinal = i;
597 #else
598                                 this[i].SetOrdinal(i);
599 #endif
600
601                         if (parentTable != null)
602                                 parentTable.OnRemoveColumn (column);
603
604                         if (column.AutoIncrement)
605                                 autoIncrement.Remove (column);
606
607                         column.PropertyChanged -= new PropertyChangedEventHandler (ColumnPropertyChanged);
608
609                         OnCollectionChanged (e);
610                 }
611
612                 /// <summary>
613                 /// Removes the DataColumn object with the specified name from the collection.
614                 /// </summary>
615                 /// <param name="name">The name of the column to remove.</param>
616                 public void Remove (string name)
617                 {
618                         DataColumn column = this [name];
619
620                         if (column == null)
621                                 throw new ArgumentException ("Column '" + name + "' does not belong to table " + ( parentTable == null ? "" : parentTable.TableName ) + ".");
622                         Remove (column);
623                 }
624
625                 /// <summary>
626                 /// Removes the column at the specified index from the collection.
627                 /// </summary>
628                 /// <param name="index">The index of the column to remove.</param>
629                 public void RemoveAt (int index)
630                 {
631                         if (Count <= index)
632                                 throw new IndexOutOfRangeException ("Cannot find column " + index + ".");
633
634                         DataColumn column = this [index];
635                         Remove (column);
636                 }
637
638                 // Helper AddRange() - Call this function when EndInit is called
639                 internal void PostAddRange ()
640                 {
641                         if (_mostRecentColumns == null)
642                                 return;
643
644                         foreach (DataColumn column in _mostRecentColumns){
645                                 if (column == null)
646                                         continue;
647                                 Add (column);
648                         }
649                         _mostRecentColumns = null;
650                 }
651
652                 internal void UpdateAutoIncrement (DataColumn col,bool isAutoIncrement)
653                 {
654                         if (isAutoIncrement) {
655                                 if (!autoIncrement.Contains (col))
656                                         autoIncrement.Add (col);
657                         } else {
658                                 if (autoIncrement.Contains (col))
659                                         autoIncrement.Remove (col);
660                         }
661                 }
662
663                 private int IndexOf (string name, bool error)
664                 {
665                         // exact case matching has already be done by the caller
666                         // Get existing doublet
667                         Doublet d = (Doublet) columnNameCount [name];
668                         if (d != null) {
669                                 if (d.count == 1) {
670                                         // There's only one
671                                         // return index of the column from the only column name of the doublet
672                                         return base.List.IndexOf (columnFromName [d.columnNames [0]]);
673                                 } else if (d.count > 1 && error) {
674                                         // there's more than one, exception!
675                                         throw new ArgumentException ("There is no match for '" + name + "' in the same case and there are multiple matches in different case.");
676                                 } else {
677                                         return -1;
678                                 }
679                         }
680                         return -1;
681                 }
682
683                 #region Events
684
685                 /// <summary>
686                 /// Occurs when the columns collection changes, either by adding or removing a column.
687                 /// </summary>
688                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
689                 public event CollectionChangeEventHandler CollectionChanged;
690
691                 internal event CollectionChangeEventHandler CollectionMetaDataChanged;
692                 #endregion
693
694                 private void OnCollectionMetaDataChanged (CollectionChangeEventArgs ccevent)
695                 {
696                         parentTable.ResetPropertyDescriptorsCache ();
697                         if (CollectionMetaDataChanged != null)
698                                 CollectionMetaDataChanged (this, ccevent);
699                 }
700
701                 private void ColumnPropertyChanged (object sender, PropertyChangedEventArgs args)
702                 {
703                         OnCollectionMetaDataChanged (new CollectionChangeEventArgs(CollectionChangeAction.Refresh, sender));
704                 }
705
706 #if NET_2_0
707                 internal void MoveColumn (int oldOrdinal, int newOrdinal)
708                 {
709                         if (newOrdinal == -1 || newOrdinal > this.Count)
710                                 throw new ArgumentOutOfRangeException ("ordinal", "Ordinal '" + newOrdinal + "' exceeds the maximum number.");
711                         if (oldOrdinal == newOrdinal)
712                                 return;
713
714                         int start = newOrdinal > oldOrdinal ? oldOrdinal : newOrdinal;
715                         int end = newOrdinal > oldOrdinal ?  newOrdinal : oldOrdinal;
716                         int direction = newOrdinal > oldOrdinal ? 1 : (-1);
717
718                         DataColumn currColumn = this [start];
719                         for (int i = start; i < end; i += direction) {
720                                 List [i] = List [i+direction];
721                                 ((DataColumn) List [i]).Ordinal = i;
722                         }
723                         List [end] = currColumn;
724                         currColumn.Ordinal = end;
725                 }
726 #endif
727         }
728 }