1 //------------------------------------------------------------------------------
2 // <copyright file="DataColumnCollection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 // <owner current="false" primary="false">Microsoft</owner>
8 //------------------------------------------------------------------------------
9 
10 namespace System.Data {
11     using System;
12     using System.Xml;
13     using System.Collections;
14     using System.Collections.Generic;
15     using System.ComponentModel;
16     using System.Data.Common;
17     using System.Diagnostics;
18 
19     /// <devdoc>
20     /// <para>Represents a collection of <see cref='System.Data.DataColumn'/>
21     /// objects for a <see cref='System.Data.DataTable'/>.</para>
22     /// </devdoc>
23     [
24     DefaultEvent("CollectionChanged"),
25     Editor("Microsoft.VSDesigner.Data.Design.ColumnsCollectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
26     ]
27     public sealed class DataColumnCollection : InternalDataCollectionBase {
28 
29         private readonly DataTable table;
30         private readonly ArrayList _list = new ArrayList();
31         private int defaultNameIndex = 1;
32         private DataColumn[] delayedAddRangeColumns;
33 
34         private readonly Dictionary<string, DataColumn> columnFromName;     // Links names to columns
35         private CollectionChangeEventHandler onCollectionChangedDelegate;
36         private CollectionChangeEventHandler onCollectionChangingDelegate;
37 
38         private CollectionChangeEventHandler onColumnPropertyChangedDelegate;
39 
40         private bool fInClear;
41 
42         private DataColumn[] columnsImplementingIChangeTracking = DataTable.zeroColumns;
43         private int nColumnsImplementingIChangeTracking = 0;
44         private int nColumnsImplementingIRevertibleChangeTracking = 0;
45 
46         /// <devdoc>
47         /// DataColumnCollection constructor.  Used only by DataTable.
48         /// </devdoc>
DataColumnCollection(DataTable table)49         internal DataColumnCollection(DataTable table) {
50             this.table = table;
51             columnFromName = new Dictionary<string, DataColumn>();
52         }
53 
54         /// <devdoc>
55         ///    <para>Gets the list of the collection items.</para>
56         /// </devdoc>
57         protected override ArrayList List {
58             get {
59                 return _list;
60             }
61         }
62 
63         internal DataColumn[] ColumnsImplementingIChangeTracking {
64             get {
65                 return columnsImplementingIChangeTracking;
66             }
67         }
68         internal int ColumnsImplementingIChangeTrackingCount{
69             get {
70                 return nColumnsImplementingIChangeTracking;
71             }
72         }
73         internal int ColumnsImplementingIRevertibleChangeTrackingCount {
74             get {
75                 return nColumnsImplementingIRevertibleChangeTracking;
76             }
77         }
78         /// <devdoc>
79         ///    <para>
80         ///       Gets the <see cref='System.Data.DataColumn'/>
81         ///       from the collection at the specified index.
82         ///    </para>
83         /// </devdoc>
84         public DataColumn this[int index] {
85             get {
86                 try { // Perf: use the readonly _list field directly and let ArrayList check the range
87                     return (DataColumn)_list[index];
88                 }
89                 catch(ArgumentOutOfRangeException) {
90                     throw ExceptionBuilder.ColumnOutOfRange(index);
91                 }
92             }
93         }
94 
95         /// <devdoc>
96         /// <para>Gets the <see cref='System.Data.DataColumn'/> from the collection with the specified name.</para>
97         /// </devdoc>
98         public DataColumn this[string name] {
99             get {
100                 if (null == name) {
101                     throw ExceptionBuilder.ArgumentNull("name");
102                 }
103                 DataColumn column;
104                 if ((!columnFromName.TryGetValue(name, out column)) || (column == null)) {
105                     // Case-Insensitive compares
106                     int index = IndexOfCaseInsensitive(name);
107                     if (0 <= index) {
108                         column = (DataColumn)_list[index];
109                     }
110                     else if (-2 == index) {
111                         throw ExceptionBuilder.CaseInsensitiveNameConflict(name);
112                     }
113                 }
114                 return column;
115             }
116         }
117 
118         internal DataColumn this[string name, string ns] {
119             get {
120                 DataColumn column;
121                 if ((columnFromName.TryGetValue(name, out column)) && (column != null) && (column.Namespace == ns)) {
122                     return column;
123                 }
124 
125                 return null;
126             }
127         }
128 
EnsureAdditionalCapacity(int capacity)129         internal void EnsureAdditionalCapacity(int capacity) {
130             if (_list.Capacity < capacity + _list.Count) {
131                 _list.Capacity = capacity + _list.Count;
132             }
133         }
134 
135         /// <devdoc>
136         /// <para>Adds the specified <see cref='System.Data.DataColumn'/>
137         /// to the columns collection.</para>
138         /// </devdoc>
Add(DataColumn column)139         public void Add(DataColumn column) {
140             AddAt(-1, column);
141         }
142 
AddAt(int index, DataColumn column)143         internal void AddAt(int index, DataColumn column) {
144             if (column != null && column.ColumnMapping == MappingType.SimpleContent) {
145                 if (table.XmlText != null && table.XmlText != column)
146                     throw ExceptionBuilder.CannotAddColumn3();
147                 if (table.ElementColumnCount > 0)
148                     throw ExceptionBuilder.CannotAddColumn4(column.ColumnName);
149                 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
150                 BaseAdd(column);
151                 if (index != -1)
152                     ArrayAdd(index, column);
153                 else
154                     ArrayAdd(column);
155 
156                 table.XmlText = column;
157             }
158             else {
159                 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
160                 BaseAdd(column);
161                 if (index != -1)
162                     ArrayAdd(index, column);
163                 else
164                     ArrayAdd(column);
165                 // if the column is an element increase the internal dataTable counter
166                 if (column.ColumnMapping == MappingType.Element)
167                     table.ElementColumnCount ++;
168             }
169             if (!table.fInitInProgress && column != null && column.Computed) {
170                 column.Expression = column.Expression;
171             }
172             OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
173         }
174         /// <devdoc>
175         ///    <para>[To be supplied.]</para>
176         /// </devdoc>
AddRange(DataColumn[] columns)177         public void AddRange(DataColumn[] columns) {
178             if (table.fInitInProgress) {
179                 delayedAddRangeColumns = columns;
180                 return;
181             }
182 
183             if (columns != null) {
184                 foreach(DataColumn column in columns) {
185                     if (column != null) {
186                         Add(column);
187                     }
188                 }
189             }
190         }
191 
192         /// <devdoc>
193         /// <para>Creates and adds a <see cref='System.Data.DataColumn'/>
194         /// with
195         /// the specified name, type, and compute expression to the columns collection.</para>
196         /// </devdoc>
Add(string columnName, Type type, string expression)197         public DataColumn Add(string columnName, Type type, string expression) {
198             DataColumn column = new DataColumn(columnName, type, expression);
199             Add(column);
200             return column;
201         }
202 
203         /// <devdoc>
204         /// <para>Creates and adds a <see cref='System.Data.DataColumn'/>
205         /// with the
206         /// specified name and type to the columns collection.</para>
207         /// </devdoc>
Add(string columnName, Type type)208         public DataColumn Add(string columnName, Type type) {
209             DataColumn column = new DataColumn(columnName, type);
210             Add(column);
211             return column;
212         }
213 
214         /// <devdoc>
215         /// <para>Creates and adds a <see cref='System.Data.DataColumn'/>
216         /// with the specified name to the columns collection.</para>
217         /// </devdoc>
Add(string columnName)218         public DataColumn Add(string columnName) {
219             DataColumn column = new DataColumn(columnName);
220             Add(column);
221             return column;
222         }
223 
224         /// <devdoc>
225         /// <para>Creates and adds a <see cref='System.Data.DataColumn'/> to a columns collection.</para>
226         /// </devdoc>
Add()227         public DataColumn Add() {
228             DataColumn column = new DataColumn();
229             Add(column);
230             return column;
231         }
232 
233 
234         /// <devdoc>
235         ///    <para>Occurs when the columns collection changes, either by adding or removing a column.</para>
236         /// </devdoc>
237         [ResDescriptionAttribute(Res.collectionChangedEventDescr)]
238         public event CollectionChangeEventHandler CollectionChanged {
239             add {
240                 onCollectionChangedDelegate += value;
241             }
242             remove {
243                 onCollectionChangedDelegate -= value;
244             }
245         }
246 
247         internal event CollectionChangeEventHandler CollectionChanging {
248             add {
249                 onCollectionChangingDelegate += value;
250             }
251             remove {
252                 onCollectionChangingDelegate -= value;
253             }
254         }
255 
256         internal event CollectionChangeEventHandler ColumnPropertyChanged {
257             add {
258                 onColumnPropertyChangedDelegate += value;
259             }
260             remove {
261                 onColumnPropertyChangedDelegate -= value;
262             }
263         }
264 
265         /// <devdoc>
266         ///  Adds the column to the columns array.
267         /// </devdoc>
ArrayAdd(DataColumn column)268         private void ArrayAdd(DataColumn column) {
269             _list.Add(column);
270             column.SetOrdinalInternal(_list.Count - 1);
271             CheckIChangeTracking(column);
272         }
273 
ArrayAdd(int index, DataColumn column)274         private void ArrayAdd(int index, DataColumn column) {
275             _list.Insert(index, column);
276             CheckIChangeTracking(column);
277         }
278 
ArrayRemove(DataColumn column)279         private void ArrayRemove(DataColumn column) {
280             column.SetOrdinalInternal(-1);
281             _list.Remove(column);
282 
283             int count = _list.Count;
284             for (int i =0; i < count; i++) {
285                 ((DataColumn) _list[i]).SetOrdinalInternal(i);
286             }
287             if (column.ImplementsIChangeTracking) {
288                 RemoveColumnsImplementingIChangeTrackingList(column);
289             }
290         }
291 
292         /// <devdoc>
293         /// Creates a new default name.
294         /// </devdoc>
AssignName()295         internal string AssignName() {
296             string newName = MakeName(defaultNameIndex++);
297 
298             while (columnFromName.ContainsKey(newName)) {
299                 newName = MakeName(defaultNameIndex++);
300             }
301 
302             return newName;
303         }
304 
305         /// <devdoc>
306         /// Does verification on the column and it's name, and points the column at the dataSet that owns this collection.
307         /// An ArgumentNullException is thrown if this column is null.  An ArgumentException is thrown if this column
308         /// already belongs to this collection, belongs to another collection.
309         /// A DuplicateNameException is thrown if this collection already has a column with the same
310         /// name (case insensitive).
311         /// </devdoc>
BaseAdd(DataColumn column)312         private void BaseAdd(DataColumn column) {
313             if (column == null)
314                 throw ExceptionBuilder.ArgumentNull("column");
315             if (column.table == table)
316                 throw ExceptionBuilder.CannotAddColumn1(column.ColumnName);
317             if (column.table != null)
318                 throw ExceptionBuilder.CannotAddColumn2(column.ColumnName);
319 
320             if (column.ColumnName.Length == 0) {
321                 column.ColumnName = AssignName();
322             }
323             RegisterColumnName(column.ColumnName, column);
324             try {
325                 column.SetTable(table);
326                 if (!table.fInitInProgress && column.Computed) {
327                     if (column.DataExpression.DependsOn(column)) {
328                         throw ExceptionBuilder.ExpressionCircular();
329                     }
330                 }
331 
332                 if (0 < table.RecordCapacity) {
333                     // adding a column to table with existing rows
334                     column.SetCapacity(table.RecordCapacity);
335                 }
336 
337                 // fill column with default value.
338                 for (int record = 0; record < table.RecordCapacity; record++) {
339                     column.InitializeRecord(record);
340                 }
341 
342                 if (table.DataSet != null) {
343                     column.OnSetDataSet();
344                 }
345             }
346             catch (Exception e) {
347                 //
348                 if (ADP.IsCatchableOrSecurityExceptionType(e)) {
349                     UnregisterName(column.ColumnName);
350                 }
351                 throw;
352             }
353         }
354 
355         /// <devdoc>
356         /// BaseGroupSwitch will intelligently remove and add tables from the collection.
357         /// </devdoc>
BaseGroupSwitch(DataColumn[] oldArray, int oldLength, DataColumn[] newArray, int newLength)358         private void BaseGroupSwitch(DataColumn[] oldArray, int oldLength, DataColumn[] newArray, int newLength) {
359             // We're doing a smart diff of oldArray and newArray to find out what
360             // should be removed.  We'll pass through oldArray and see if it exists
361             // in newArray, and if not, do remove work.  newBase is an opt. in case
362             // the arrays have similar prefixes.
363             int newBase = 0;
364             for (int oldCur = 0; oldCur < oldLength; oldCur++) {
365                 bool found = false;
366                 for (int newCur = newBase; newCur < newLength; newCur++) {
367                     if (oldArray[oldCur] == newArray[newCur]) {
368                         if (newBase == newCur) {
369                             newBase++;
370                         }
371                         found = true;
372                         break;
373                     }
374                 }
375                 if (!found) {
376                     // This means it's in oldArray and not newArray.  Remove it.
377                     if (oldArray[oldCur].Table == table) {
378                         BaseRemove(oldArray[oldCur]);
379                         _list.Remove(oldArray[oldCur]);
380                         oldArray[oldCur].SetOrdinalInternal(-1);
381                     }
382                 }
383             }
384 
385             // Now, let's pass through news and those that don't belong, add them.
386             for (int newCur = 0; newCur < newLength; newCur++) {
387                 if (newArray[newCur].Table != table) {
388                     BaseAdd(newArray[newCur]);
389                     _list.Add(newArray[newCur]);
390                 }
391                 newArray[newCur].SetOrdinalInternal(newCur);
392             }
393         }
394 
395         /// <devdoc>
396         /// Does verification on the column and it's name, and clears the column's dataSet pointer.
397         /// An ArgumentNullException is thrown if this column is null.  An ArgumentException is thrown
398         /// if this column doesn't belong to this collection or if this column is part of a relationship.
399         /// An ArgumentException is thrown if another column's compute expression depends on this column.
400         /// </devdoc>
BaseRemove(DataColumn column)401         private void BaseRemove(DataColumn column) {
402             if (CanRemove(column, true)) {
403 
404                 // remove
405                 if (column.errors > 0) {
406                     for (int i = 0; i < table.Rows.Count; i++) {
407                         table.Rows[i].ClearError(column);
408                     }
409                 }
410                 UnregisterName(column.ColumnName);
411                 column.SetTable(null);
412             }
413         }
414 
415         /// <devdoc>
416         ///    <para>Checks
417         ///       if
418         ///       a given column can be removed from the collection.</para>
419         /// </devdoc>
CanRemove(DataColumn column)420         public bool CanRemove(DataColumn column) {
421             return CanRemove(column, false);
422         }
423 
CanRemove(DataColumn column, bool fThrowException)424         internal bool CanRemove(DataColumn column, bool fThrowException) {
425             if (column == null) {
426                 if (!fThrowException)
427                     return false;
428                 else
429                 throw ExceptionBuilder.ArgumentNull("column");
430             }
431             if (column.table != table) {
432                 if (!fThrowException)
433                     return false;
434                 else
435                 throw ExceptionBuilder.CannotRemoveColumn();
436             }
437 
438             // allow subclasses to complain first.
439             table.OnRemoveColumnInternal(column);
440 
441             // We need to make sure the column is not involved in any Relations or Constriants
442             if (table.primaryKey != null && table.primaryKey.Key.ContainsColumn(column)) {
443                 if (!fThrowException)
444                     return false;
445                 else
446                 throw ExceptionBuilder.CannotRemovePrimaryKey();
447             }
448             for (int i = 0; i < table.ParentRelations.Count; i++) {
449                 if (table.ParentRelations[i].ChildKey.ContainsColumn(column)) {
450                     if (!fThrowException)
451                         return false;
452                     else
453                     throw ExceptionBuilder.CannotRemoveChildKey(table.ParentRelations[i].RelationName);
454                 }
455             }
456             for (int i = 0; i < table.ChildRelations.Count; i++) {
457                 if (table.ChildRelations[i].ParentKey.ContainsColumn(column)) {
458                     if (!fThrowException)
459                         return false;
460                     else
461                     throw ExceptionBuilder.CannotRemoveChildKey(table.ChildRelations[i].RelationName);
462                 }
463             }
464             for (int i = 0; i < table.Constraints.Count; i++) {
465                 if (table.Constraints[i].ContainsColumn(column))
466                     if (!fThrowException)
467                         return false;
468                     else
469                     throw ExceptionBuilder.CannotRemoveConstraint(table.Constraints[i].ConstraintName, table.Constraints[i].Table.TableName);
470             }
471             if (table.DataSet != null) {
472                 for (ParentForeignKeyConstraintEnumerator en = new ParentForeignKeyConstraintEnumerator(table.DataSet, table); en.GetNext();) {
473                     Constraint constraint = en.GetConstraint();
474                     if (((ForeignKeyConstraint)constraint).ParentKey.ContainsColumn(column))
475                         if (!fThrowException)
476                             return false;
477                         else
478                             throw ExceptionBuilder.CannotRemoveConstraint(constraint.ConstraintName, constraint.Table.TableName);
479                 }
480             }
481 
482             if (column.dependentColumns != null) {
483                 for (int i = 0; i < column.dependentColumns.Count; i++) {
484                     DataColumn col = column.dependentColumns[i];
485                     if (fInClear && (col.Table == table || col.Table == null))
486                         continue;
487                     if (col.Table == null)
488                         continue;
489                     Debug.Assert(col.Computed, "invalid (non an expression) column in the expression dependent columns");
490                     DataExpression expr = col.DataExpression;
491                     if ((expr!= null) && (expr.DependsOn(column))) {
492                         if (!fThrowException)
493                             return false;
494                         else
495                             throw ExceptionBuilder.CannotRemoveExpression(col.ColumnName, col.Expression);
496                     }
497                 }
498             }
499 
500             // SQLBU 429176: you can't remove a column participating in an index,
501             // while index events are suspended else the indexes won't be properly maintained.
502             // However, all the above checks should catch those participating columns.
503             // except when a column is in a DataView RowFilter or Sort clause
504             foreach (Index index in table.LiveIndexes) {
505 #if false
506                 if (!Object.ReferenceEquals(index, column.sortIndex)) {
507                     foreach (IndexField field in index.IndexFields) {
508                         if (Object.ReferenceEquals(field.Column, column)) {
509                             if (fThrowException) {
510                                 throw ExceptionBuilder.CannotRemoveExpression("DataView", column.ColumnName);
511                             }
512                             return false;
513                         }
514                     }
515                 }
516 #endif
517             }
518 
519             return true;
520         }
521 
CheckIChangeTracking(DataColumn column)522         private void CheckIChangeTracking(DataColumn column) {
523             if (column.ImplementsIRevertibleChangeTracking) {
524                 nColumnsImplementingIRevertibleChangeTracking++;
525                 nColumnsImplementingIChangeTracking++;
526                 AddColumnsImplementingIChangeTrackingList(column);
527             }
528             else if (column.ImplementsIChangeTracking) {
529                 nColumnsImplementingIChangeTracking++;
530                 AddColumnsImplementingIChangeTrackingList(column);
531             }
532         }
533 
534         /// <devdoc>
535         ///    <para>
536         ///       Clears the collection of any columns.
537         ///    </para>
538         /// </devdoc>
Clear()539         public void Clear() {
540             int oldLength = _list.Count;
541 
542             DataColumn[] columns = new DataColumn[_list.Count];
543             _list.CopyTo(columns, 0);
544 
545             OnCollectionChanging(RefreshEventArgs);
546 
547             if (table.fInitInProgress && delayedAddRangeColumns != null) {
548                 delayedAddRangeColumns = null;
549             }
550 
551             try {
552                 // this will smartly add and remove the appropriate tables.
553                 fInClear = true;
554                 BaseGroupSwitch(columns, oldLength, null, 0);
555                 fInClear = false;
556             }
557             catch (Exception e) {
558                 //
559                 if (ADP.IsCatchableOrSecurityExceptionType(e)) {
560                     // something messed up: restore to old values and throw
561                     fInClear = false;
562                     BaseGroupSwitch(null, 0, columns, oldLength);
563                     _list.Clear();
564                     for (int i = 0; i < oldLength; i++)
565                         _list.Add(columns[i]);
566                 }
567                 throw;
568             }
569             _list.Clear();
570             table.ElementColumnCount  = 0;
571             OnCollectionChanged(RefreshEventArgs);
572         }
573 
574         /// <devdoc>
575         ///    <para>Checks whether the collection contains a column with the specified name.</para>
576         /// </devdoc>
Contains(string name)577         public bool Contains(string name) {
578             DataColumn column;
579             if ((columnFromName.TryGetValue(name, out column)) && (column != null)) {
580                 return true;
581             }
582 
583             return (IndexOfCaseInsensitive(name) >= 0);
584         }
585 
Contains(string name, bool caseSensitive)586         internal bool Contains(string name, bool caseSensitive) {
587             DataColumn column;
588             if ((columnFromName.TryGetValue(name, out column)) && (column != null)) {
589                 return true;
590             }
591 
592             if (caseSensitive) { // above check did case sensitive check
593                 return false;
594             }
595             else {
596                 return (IndexOfCaseInsensitive(name) >= 0);
597             }
598         }
599 
CopyTo(DataColumn[] array, int index)600         public void CopyTo(DataColumn[] array, int index) {
601             if (array==null)
602                 throw ExceptionBuilder.ArgumentNull("array");
603             if (index < 0)
604                 throw ExceptionBuilder.ArgumentOutOfRange("index");
605             if (array.Length - index < _list.Count)
606                 throw ExceptionBuilder.InvalidOffsetLength();
607             for(int i = 0; i < _list.Count; ++i) {
608                 array[index + i] = (DataColumn)_list[i];
609             }
610         }
611 
612         /// <devdoc>
613         ///    <para>
614         ///       Returns the index of a specified <see cref='System.Data.DataColumn'/>.
615         ///    </para>
616         /// </devdoc>
IndexOf(DataColumn column)617         public int IndexOf(DataColumn column) {
618             int columnCount = _list.Count;
619             for (int i = 0; i < columnCount; ++i) {
620                 if (column == (DataColumn) _list[i]) {
621                     return i;
622                 }
623             }
624             return -1;
625         }
626 
627         /// <devdoc>
628         ///    <para>Returns the index of
629         ///       a column specified by name.</para>
630         /// </devdoc>
IndexOf(string columnName)631         public int IndexOf(string columnName) {
632 
633             if ((null != columnName) && (0 < columnName.Length)) {
634                 int count = Count;
635                 DataColumn column;
636                 if ((columnFromName.TryGetValue(columnName, out column)) && (column != null)) {
637                     for (int j = 0; j < count; j++)
638                         if (column == _list[j]) {
639                             return j;
640                     }
641                 }
642                 else {
643                     int res = IndexOfCaseInsensitive(columnName);
644                     return (res < 0) ? -1 : res;
645                 }
646             }
647             return -1;
648         }
649 
IndexOfCaseInsensitive(string name)650         internal int IndexOfCaseInsensitive (string name) {
651             int hashcode = table.GetSpecialHashCode(name);
652             int cachedI = -1;
653             DataColumn column = null;
654             for (int i = 0; i < Count; i++) {
655                 column = (DataColumn) _list[i];
656                 if ( (hashcode == 0 || column._hashCode == 0 || column._hashCode == hashcode) &&
657                    NamesEqual(column.ColumnName, name, false, table.Locale) != 0 ) {
658                     if (cachedI == -1)
659                         cachedI = i;
660                     else
661                         return -2;
662                 }
663             }
664             return cachedI;
665         }
666 
FinishInitCollection()667         internal void FinishInitCollection() {
668             if (delayedAddRangeColumns != null) {
669                 foreach(DataColumn column in delayedAddRangeColumns) {
670                     if (column != null) {
671                         Add(column);
672                     }
673                 }
674 
675                 foreach(DataColumn column in delayedAddRangeColumns) {
676                     if (column != null) {
677                         column.FinishInitInProgress();
678                     }
679                 }
680 
681                 delayedAddRangeColumns = null;
682             }
683         }
684 
685         /// <devdoc>
686         /// Makes a default name with the given index.  e.g. Column1, Column2, ... Columni
687         /// </devdoc>
MakeName(int index)688         private string MakeName(int index) {
689             if (1 == index) {
690                 return "Column1";
691             }
692             return "Column" + index.ToString(System.Globalization.CultureInfo.InvariantCulture);
693         }
694 
MoveTo(DataColumn column, int newPosition)695         internal void MoveTo(DataColumn column, int newPosition) {
696             if (0 > newPosition || newPosition > Count -1) {
697                 throw ExceptionBuilder.InvalidOrdinal("ordinal", newPosition);
698             }
699             if (column.ImplementsIChangeTracking) {
700                 RemoveColumnsImplementingIChangeTrackingList(column);
701             }
702             _list.Remove(column);
703             _list.Insert(newPosition, column);
704             int count = _list.Count;
705             for (int i =0; i < count; i++) {
706                 ((DataColumn) _list[i]).SetOrdinalInternal(i);
707             }
708             CheckIChangeTracking(column);
709             OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, column));
710         }
711 
712         /// <devdoc>
713         ///    <para>
714         ///       Raises the <see cref='System.Data.DataColumnCollection.OnCollectionChanged'/> event.
715         ///    </para>
716         /// </devdoc>
OnCollectionChanged(CollectionChangeEventArgs ccevent)717         private void OnCollectionChanged(CollectionChangeEventArgs ccevent) {
718             table.UpdatePropertyDescriptorCollectionCache();
719 
720             if ((null != ccevent) && !table.SchemaLoading && !table.fInitInProgress) {
721                 DataColumn column = (DataColumn)ccevent.Element;
722             }
723             if (onCollectionChangedDelegate != null) {
724                 onCollectionChangedDelegate(this, ccevent);
725             }
726         }
727 
728         /// <devdoc>
729         ///    <para>[To be supplied.]</para>
730         /// </devdoc>
OnCollectionChanging(CollectionChangeEventArgs ccevent)731         private void OnCollectionChanging(CollectionChangeEventArgs ccevent) {
732             if (onCollectionChangingDelegate != null) {
733                 onCollectionChangingDelegate(this, ccevent);
734             }
735         }
736 
OnColumnPropertyChanged(CollectionChangeEventArgs ccevent)737         internal void OnColumnPropertyChanged(CollectionChangeEventArgs ccevent) {
738             table.UpdatePropertyDescriptorCollectionCache();
739             if (onColumnPropertyChangedDelegate != null) {
740                 onColumnPropertyChangedDelegate(this, ccevent);
741             }
742         }
743 
744         /// <devdoc>
745         /// Registers this name as being used in the collection.  Will throw an ArgumentException
746         /// if the name is already being used.  Called by Add, All property, and Column.ColumnName property.
747         /// if the name is equivalent to the next default name to hand out, we increment our defaultNameIndex.
748         /// NOTE: To add a child table, pass column as null
749         /// </devdoc>
RegisterColumnName(string name, DataColumn column)750         internal void RegisterColumnName(string name, DataColumn column) {
751             Debug.Assert (name != null);
752 
753             try {
754                 columnFromName.Add(name, column);
755 
756                 if (null != column) {
757                     column._hashCode = table.GetSpecialHashCode(name);
758                 }
759             }
760             catch (ArgumentException) { // Argument exception means that there is already an existing key
761                 if (columnFromName[name] != null) {
762                     if (column != null) {
763                         throw ExceptionBuilder.CannotAddDuplicate(name);
764                     }
765                     else {
766                         throw ExceptionBuilder.CannotAddDuplicate3(name);
767                     }
768                 }
769                 throw ExceptionBuilder.CannotAddDuplicate2(name);
770             }
771 
772             // If we're adding a child table, then update defaultNameIndex to avoid colisions between the child table and auto-generated column names
773             if ((column == null) && NamesEqual(name, MakeName(defaultNameIndex), true, table.Locale) != 0) {
774                 do {
775                     defaultNameIndex++;
776                 } while (Contains(MakeName(defaultNameIndex)));
777             }
778         }
779 
CanRegisterName(string name)780         internal bool CanRegisterName(string name) {
781             Debug.Assert (name != null, "Must specify a name");
782             return (!columnFromName.ContainsKey(name));
783         }
784 
785         /// <devdoc>
786         /// <para>Removes the specified <see cref='System.Data.DataColumn'/>
787         /// from the collection.</para>
788         /// </devdoc>
Remove(DataColumn column)789         public void Remove(DataColumn column) {
790             OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Remove, column));
791             BaseRemove(column);
792             ArrayRemove(column);
793             OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, column));
794             // if the column is an element decrease the internal dataTable counter
795             if (column.ColumnMapping == MappingType.Element)
796                 table.ElementColumnCount --;
797         }
798 
799         /// <devdoc>
800         ///    <para>Removes the
801         ///       column at the specified index from the collection.</para>
802         /// </devdoc>
RemoveAt(int index)803         public void RemoveAt(int index) {
804             DataColumn dc = this[index];
805             if (dc == null)
806                 throw ExceptionBuilder.ColumnOutOfRange(index);
807             Remove(dc);
808         }
809 
810         /// <devdoc>
811         ///    <para>Removes the
812         ///       column with the specified name from the collection.</para>
813         /// </devdoc>
Remove(string name)814         public void Remove(string name) {
815             DataColumn dc = this[name];
816             if (dc == null)
817                 throw ExceptionBuilder.ColumnNotInTheTable(name, table.TableName);
818             Remove(dc);
819         }
820 
821         /// <devdoc>
822         /// Unregisters this name as no longer being used in the collection.  Called by Remove, All property, and
823         /// Column.ColumnName property.  If the name is equivalent to the last proposed default name, we walk backwards
824         /// to find the next proper default name to use.
825         /// </devdoc>
UnregisterName(string name)826         internal void UnregisterName(string name) {
827             columnFromName.Remove(name);
828 
829             if (NamesEqual(name, MakeName(defaultNameIndex - 1), true, table.Locale) != 0) {
830                 do {
831                     defaultNameIndex--;
832                 } while (defaultNameIndex > 1 &&
833                          !Contains(MakeName(defaultNameIndex - 1)));
834             }
835         }
836 
AddColumnsImplementingIChangeTrackingList(DataColumn dataColumn)837         private void AddColumnsImplementingIChangeTrackingList(DataColumn dataColumn) {
838             DataColumn[] columns = columnsImplementingIChangeTracking;
839             DataColumn[] tempColumns = new DataColumn[columns.Length +1];
840             columns.CopyTo(tempColumns, 0);
841             tempColumns[columns.Length] = dataColumn;
842             columnsImplementingIChangeTracking = tempColumns;
843         }
844 
RemoveColumnsImplementingIChangeTrackingList(DataColumn dataColumn)845         private void RemoveColumnsImplementingIChangeTrackingList(DataColumn dataColumn) {
846             DataColumn[] columns = columnsImplementingIChangeTracking;
847             DataColumn[] tempColumns = new DataColumn[columns.Length - 1];
848             for(int i = 0, j = 0; i < columns.Length; i++) {
849                 if (columns[i] != dataColumn) {
850                     tempColumns[j++] = columns[i];
851                 }
852             }
853             columnsImplementingIChangeTracking = tempColumns;
854         }
855     }
856 }
857