Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / DataTableCollection.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DataTableCollection.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.Diagnostics;
13     using System.Collections;
14     using System.ComponentModel;
15     using System.Globalization;
16
17     /// <devdoc>
18     ///    <para>
19     ///       Represents the collection of tables for the <see cref='System.Data.DataSet'/>.
20     ///    </para>
21     /// </devdoc>
22     [
23     DefaultEvent("CollectionChanged"),
24     Editor("Microsoft.VSDesigner.Data.Design.TablesCollectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
25     ListBindable(false),
26     ]
27     public sealed class DataTableCollection : InternalDataCollectionBase {
28
29         private readonly DataSet dataSet      = null;
30         // private DataTable[] tables   = new DataTable[2];
31         // private int tableCount       = 0;
32         private readonly ArrayList _list = new ArrayList();
33         private int defaultNameIndex = 1;
34         private DataTable[] delayedAddRangeTables = null;
35
36         private CollectionChangeEventHandler onCollectionChangedDelegate = null;
37         private CollectionChangeEventHandler onCollectionChangingDelegate = null;
38
39         private static int _objectTypeCount; // Bid counter
40         private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
41
42         /// <devdoc>
43         /// DataTableCollection constructor.  Used only by DataSet.
44         /// </devdoc>
45         internal DataTableCollection(DataSet dataSet) {
46             Bid.Trace("<ds.DataTableCollection.DataTableCollection|INFO> %d#, dataSet=%d\n", ObjectID, (dataSet != null) ? dataSet.ObjectID : 0);
47             this.dataSet = dataSet;
48         }
49
50         /// <devdoc>
51         ///    <para>
52         ///       Gets the tables
53         ///       in the collection as an object.
54         ///    </para>
55         /// </devdoc>
56         protected override ArrayList List {
57             get {
58                 return _list;
59             }
60         }
61
62         internal int ObjectID {
63             get {
64                 return _objectID;
65             }
66         }
67
68         /// <devdoc>
69         ///    <para>Gets the table specified by its index.</para>
70         /// </devdoc>
71         public DataTable this[int index] {
72             get {
73                 try { // Perf: use the readonly _list field directly and let ArrayList check the range
74                     return(DataTable) _list[index];
75                 }
76                 catch(ArgumentOutOfRangeException) {
77                     throw ExceptionBuilder.TableOutOfRange(index);
78                 }
79             }
80         }
81
82         /// <devdoc>
83         ///    <para>Gets the table in the collection with the given name (not case-sensitive).</para>
84         /// </devdoc>
85         public DataTable this[string name] {
86             get {
87                 int index = InternalIndexOf(name);
88                 if (index == -2) {
89                     throw ExceptionBuilder.CaseInsensitiveNameConflict(name);
90                 }
91                 if (index == -3) {
92                     throw ExceptionBuilder.NamespaceNameConflict(name);
93                 }
94                 return (index < 0) ? null : (DataTable)_list[index];
95             }
96         }
97
98         public DataTable this[string name, string tableNamespace] {
99             get {
100                 if (tableNamespace == null)
101                     throw ExceptionBuilder.ArgumentNull("tableNamespace");
102                 int index = InternalIndexOf(name, tableNamespace);
103                 if (index == -2) {
104                     throw ExceptionBuilder.CaseInsensitiveNameConflict(name);
105                 }
106                 return (index < 0) ? null : (DataTable)_list[index];
107             }
108         }
109
110         // Case-sensitive search in Schema, data and diffgram loading
111         internal DataTable GetTable(string name, string ns)
112         {
113             for (int i = 0; i < _list.Count; i++) {
114                 DataTable   table = (DataTable) _list[i];
115                 if (table.TableName == name && table.Namespace == ns)
116                     return table;
117             }
118             return null;
119         }
120
121         // Case-sensitive smart search: it will look for a table using the ns only if required to
122         // resolve a conflict
123         internal DataTable GetTableSmart(string name, string ns){
124             int fCount = 0;
125             DataTable fTable = null;
126             for (int i = 0; i < _list.Count; i++) {
127                 DataTable   table = (DataTable) _list[i];
128                 if (table.TableName == name) {
129                     if (table.Namespace == ns)
130                         return table;
131                     fCount++;
132                     fTable = table;
133                 }
134             }
135             // if we get here we didn't match the namespace
136             // so return the table only if fCount==1 (it's the only one)
137             return (fCount == 1) ? fTable : null;
138         }
139         /// <devdoc>
140         ///    <para>
141         ///       Adds
142         ///       the specified table to the collection.
143         ///    </para>
144         /// </devdoc>
145         public void Add(DataTable table) {
146             IntPtr hscp;
147             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.Add|API> %d#, table=%d\n", ObjectID, (table!= null) ?  table.ObjectID : 0);
148             try {
149                 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, table));
150                 BaseAdd(table);
151                 ArrayAdd(table);
152
153                 if (table.SetLocaleValue(dataSet.Locale, false, false) || 
154                     table.SetCaseSensitiveValue(dataSet.CaseSensitive, false, false)) {
155                     table.ResetIndexes();
156                 }
157                 OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, table));
158             }
159             finally {
160                 Bid.ScopeLeave(ref hscp);
161             }
162         }
163
164         /// <devdoc>
165         ///    <para>[To be supplied.]</para>
166         /// </devdoc>
167         public void AddRange(DataTable[] tables) {
168             IntPtr hscp;
169             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.AddRange|API> %d#\n", ObjectID);
170             try {
171                 if (dataSet.fInitInProgress) {
172                     delayedAddRangeTables = tables;
173                     return;
174                 }
175
176                 if (tables != null) {
177                     foreach(DataTable table in tables) {
178                         if (table != null) {
179                             Add(table);
180                         }
181                     }
182                 }
183             }
184             finally{
185                 Bid.ScopeLeave(ref hscp);
186             }
187         }
188
189         /// <devdoc>
190         ///    <para>
191         ///       Creates a table with the given name and adds it to the
192         ///       collection.
193         ///    </para>
194         /// </devdoc>
195         public DataTable Add(string name) {
196             DataTable table = new DataTable(name);
197             // fxcop: new DataTable should inherit the CaseSensitive, Locale, Namespace from DataSet
198             Add(table);
199             return table;
200         }
201
202         public DataTable Add(string name, string tableNamespace) {
203             DataTable table = new DataTable(name, tableNamespace);
204             // fxcop: new DataTable should inherit the CaseSensitive, Locale from DataSet
205             Add(table);
206             return table;
207         }
208         /// <devdoc>
209         ///    <para>
210         ///       Creates a new table with a default name and adds it to
211         ///       the collection.
212         ///    </para>
213         /// </devdoc>
214         public DataTable Add() {
215             DataTable table = new DataTable();
216             // fxcop: new DataTable should inherit the CaseSensitive, Locale, Namespace from DataSet
217             Add(table);
218             return table;
219         }
220
221         /// <devdoc>
222         ///    <para>
223         ///       Occurs when the collection is changed.
224         ///    </para>
225         /// </devdoc>
226         [ResDescriptionAttribute(Res.collectionChangedEventDescr)]
227         public event CollectionChangeEventHandler CollectionChanged {
228             add {
229                 Bid.Trace("<ds.DataTableCollection.add_CollectionChanged|API> %d#\n", ObjectID);
230                 onCollectionChangedDelegate += value;
231             }
232             remove {
233                 Bid.Trace("<ds.DataTableCollection.remove_CollectionChanged|API> %d#\n", ObjectID);
234                 onCollectionChangedDelegate -= value;
235             }
236         }
237
238         /// <devdoc>
239         ///    <para>[To be supplied.]</para>
240         /// </devdoc>
241         public event CollectionChangeEventHandler CollectionChanging {
242             add {
243                 Bid.Trace("<ds.DataTableCollection.add_CollectionChanging|API> %d#\n", ObjectID);
244                 onCollectionChangingDelegate += value;
245             }
246             remove {
247                 Bid.Trace("<ds.DataTableCollection.remove_CollectionChanging|API> %d#\n", ObjectID);
248                 onCollectionChangingDelegate -= value;
249             }
250         }
251
252         /// <devdoc>
253         ///  Adds the table to the tables array.
254         /// </devdoc>
255         private void ArrayAdd(DataTable table) {
256             _list.Add(table);
257         }
258
259         /// <devdoc>
260         /// Creates a new default name.
261         /// </devdoc>
262         internal string AssignName() {
263             string newName = null;
264             // RAIDBUG: 91671
265             while(this.Contains( newName = MakeName(defaultNameIndex)))
266                 defaultNameIndex++;
267             return newName;
268         }
269
270         /// <devdoc>
271         /// Does verification on the table and it's name, and points the table at the dataSet that owns this collection.
272         /// An ArgumentNullException is thrown if this table is null.  An ArgumentException is thrown if this table
273         /// already belongs to this collection, belongs to another collection.
274         /// A DuplicateNameException is thrown if this collection already has a table with the same
275         /// name (case insensitive).
276         /// </devdoc>
277         private void BaseAdd(DataTable table) {
278             if (table == null)
279                 throw ExceptionBuilder.ArgumentNull("table");
280             if (table.DataSet == dataSet)
281                 throw ExceptionBuilder.TableAlreadyInTheDataSet();
282             if (table.DataSet != null)
283                 throw ExceptionBuilder.TableAlreadyInOtherDataSet();
284
285             if (table.TableName.Length == 0)
286                 table.TableName = AssignName();
287             else {
288                 if (NamesEqual(table.TableName, dataSet.DataSetName, false, dataSet.Locale) != 0 && !table.fNestedInDataset)
289                    throw ExceptionBuilder.DatasetConflictingName(dataSet.DataSetName);
290                 RegisterName(table.TableName, table.Namespace);
291             }
292
293             table.SetDataSet(dataSet);
294
295             //must run thru the document incorporating the addition of this data table
296             //must make sure there is no other schema component which have the same
297             // identity as this table (for example, there must not be a table with the
298             // same identity as a column in this schema.
299         }
300
301         /// <devdoc>
302         /// BaseGroupSwitch will intelligently remove and add tables from the collection.
303         /// </devdoc>
304         private void BaseGroupSwitch(DataTable[] oldArray, int oldLength, DataTable[] newArray, int newLength) {
305             // We're doing a smart diff of oldArray and newArray to find out what
306             // should be removed.  We'll pass through oldArray and see if it exists
307             // in newArray, and if not, do remove work.  newBase is an opt. in case
308             // the arrays have similar prefixes.
309             int newBase = 0;
310             for (int oldCur = 0; oldCur < oldLength; oldCur++) {
311                 bool found = false;
312                 for (int newCur = newBase; newCur < newLength; newCur++) {
313                     if (oldArray[oldCur] == newArray[newCur]) {
314                         if (newBase == newCur) {
315                             newBase++;
316                         }
317                         found = true;
318                         break;
319                     }
320                 }
321                 if (!found) {
322                     // This means it's in oldArray and not newArray.  Remove it.
323                     if (oldArray[oldCur].DataSet == dataSet) {
324                         BaseRemove(oldArray[oldCur]);
325                     }
326                 }
327             }
328
329             // Now, let's pass through news and those that don't belong, add them.
330             for (int newCur = 0; newCur < newLength; newCur++) {
331                 if (newArray[newCur].DataSet != dataSet) {
332                     BaseAdd(newArray[newCur]);
333                     _list.Add(newArray[newCur]);
334                 }
335             }
336         }
337
338         /// <devdoc>
339         /// Does verification on the table and it's name, and clears the table's dataSet pointer.
340         /// An ArgumentNullException is thrown if this table is null.  An ArgumentException is thrown
341         /// if this table doesn't belong to this collection or if this table is part of a relationship.
342         /// </devdoc>
343         private void BaseRemove(DataTable table) {
344             if (CanRemove(table, true)) {
345                 UnregisterName(table.TableName);
346                 table.SetDataSet(null);
347
348             }
349             _list.Remove(table);
350             dataSet.OnRemovedTable(table);
351         }
352
353         /// <devdoc>
354         ///    <para>
355         ///       Verifies if a given table can be removed from the collection.
356         ///    </para>
357         /// </devdoc>
358         public bool CanRemove(DataTable table) {
359             return CanRemove(table, false);
360         }
361
362         internal bool CanRemove(DataTable table, bool fThrowException) {
363             IntPtr hscp;
364             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.CanRemove|INFO> %d#, table=%d, fThrowException=%d{bool}\n", ObjectID, (table != null)? table.ObjectID : 0 , fThrowException);
365             try {
366                 if (table == null) {
367                     if (!fThrowException)
368                         return false;
369                     else
370                         throw ExceptionBuilder.ArgumentNull("table");
371                 }
372                 if (table.DataSet != dataSet) {
373                     if (!fThrowException)
374                         return false;
375                     else
376                         throw ExceptionBuilder.TableNotInTheDataSet(table.TableName);
377                 }
378
379                 // allow subclasses to throw.
380                 dataSet.OnRemoveTable(table);
381
382                 if (table.ChildRelations.Count != 0 || table.ParentRelations.Count != 0) {
383                     if (!fThrowException)
384                         return false;
385                     else
386                         throw ExceptionBuilder.TableInRelation();
387                 }
388
389                 for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, table); constraints.GetNext();) {
390                     ForeignKeyConstraint constraint = constraints.GetForeignKeyConstraint();
391                     if (constraint.Table == table && constraint.RelatedTable == table) // we can go with (constraint.Table ==  constraint.RelatedTable)
392                         continue;
393                     if (!fThrowException)
394                         return false;
395                     else
396                         throw ExceptionBuilder.TableInConstraint(table, constraint);
397                 }
398
399                 for (ChildForeignKeyConstraintEnumerator constraints = new ChildForeignKeyConstraintEnumerator(dataSet, table); constraints.GetNext();) {
400                     ForeignKeyConstraint constraint = constraints.GetForeignKeyConstraint();
401                     if (constraint.Table == table && constraint.RelatedTable == table) // bug 97670
402                         continue;
403
404                     if (!fThrowException)
405                         return false;
406                     else
407                         throw ExceptionBuilder.TableInConstraint(table, constraint);
408                 }
409
410                 return true;
411             }
412             finally{
413                 Bid.ScopeLeave(ref hscp);
414             }
415         }
416
417         /// <devdoc>
418         ///    <para>
419         ///       Clears the collection of any tables.
420         ///    </para>
421         /// </devdoc>
422         public void Clear() {
423             IntPtr hscp;
424             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.Clear|API> %d#\n", ObjectID);
425             try {
426                 int oldLength = _list.Count;
427                 DataTable[] tables = new DataTable[_list.Count];
428                 _list.CopyTo(tables, 0);
429
430                 OnCollectionChanging(RefreshEventArgs);
431
432                 if (dataSet.fInitInProgress && delayedAddRangeTables != null) {
433                     delayedAddRangeTables = null;
434                 }
435
436                 BaseGroupSwitch(tables, oldLength, null, 0);
437                 _list.Clear();
438
439                 OnCollectionChanged(RefreshEventArgs);
440                 }
441             finally {
442                 Bid.ScopeLeave(ref hscp);
443             }
444         }
445
446         /// <devdoc>
447         ///    <para>
448         ///       Checks if a table, specified by name, exists in the collection.
449         ///    </para>
450         /// </devdoc>
451         public bool Contains(string name) {
452             return (InternalIndexOf(name) >= 0);
453         }
454
455         public bool Contains(string name, string tableNamespace) {
456             if (name == null)
457                 throw ExceptionBuilder.ArgumentNull("name");
458
459             if (tableNamespace == null)
460                 throw ExceptionBuilder.ArgumentNull("tableNamespace");
461
462             return (InternalIndexOf(name, tableNamespace) >= 0);
463         }
464
465         internal bool Contains(string name, string tableNamespace, bool checkProperty, bool caseSensitive) {
466             if (!caseSensitive)
467                 return (InternalIndexOf(name) >= 0);
468
469             // Case-Sensitive compare
470             int count = _list.Count;
471             for (int i = 0; i < count; i++) {
472                 DataTable table = (DataTable) _list[i];
473                 // this may be needed to check wether the cascading is creating some conflicts
474                 string ns = checkProperty ? table.Namespace : table.tableNamespace ;
475                 if (NamesEqual(table.TableName, name, true, dataSet.Locale) == 1 && (ns == tableNamespace))
476                     return true;
477             }
478             return false;
479         }
480
481         internal bool Contains(string name, bool caseSensitive) {
482             if (!caseSensitive)
483                 return (InternalIndexOf(name) >= 0);
484
485             // Case-Sensitive compare
486             int count = _list.Count;
487             for (int i = 0; i < count; i++) {
488                 DataTable table = (DataTable) _list[i];
489                 if (NamesEqual(table.TableName, name, true, dataSet.Locale) == 1 )
490                     return true;
491             }
492             return false;
493         }
494
495         public void CopyTo(DataTable[] array, int index) {
496             if (array==null)
497                 throw ExceptionBuilder.ArgumentNull("array");
498             if (index < 0)
499                 throw ExceptionBuilder.ArgumentOutOfRange("index");
500             if (array.Length - index < _list.Count)
501                 throw ExceptionBuilder.InvalidOffsetLength();
502             for(int i = 0; i < _list.Count; ++i) {
503                 array[index + i] = (DataTable)_list[i];
504             }
505         }
506
507         /// <devdoc>
508         ///    <para>
509         ///       Returns the index of a specified <see cref='System.Data.DataTable'/>.
510         ///    </para>
511         /// </devdoc>
512         public int IndexOf(DataTable table) {
513             int tableCount = _list.Count;
514             for (int i = 0; i < tableCount; ++i) {
515                 if (table == (DataTable) _list[i]) {
516                     return i;
517                 }
518             }
519             return -1;
520         }
521
522         /// <devdoc>
523         ///    <para>
524         ///       Returns the index of the
525         ///       table with the given name (case insensitive), or -1 if the table
526         ///       doesn't exist in the collection.
527         ///    </para>
528         /// </devdoc>
529         public int IndexOf(string tableName) {
530             int index = InternalIndexOf(tableName);
531             return (index < 0) ? -1 : index;
532         }
533
534         public int IndexOf(string tableName, string tableNamespace) {
535             return IndexOf( tableName, tableNamespace, true);
536         }
537
538         internal int IndexOf(string tableName, string tableNamespace, bool chekforNull) { // this should be public! why it is missing?
539             if (chekforNull) {
540                 if (tableName == null)
541                     throw ExceptionBuilder.ArgumentNull("tableName");
542                 if (tableNamespace == null)
543                     throw ExceptionBuilder.ArgumentNull("tableNamespace");
544             }
545             int index = InternalIndexOf(tableName, tableNamespace);
546             return (index < 0) ? -1 : index;
547         }
548
549         internal void ReplaceFromInference(System.Collections.Generic.List<DataTable> tableList) {
550             Debug.Assert(_list.Count == tableList.Count, "Both lists should have equal numbers of tables");
551             _list.Clear();
552             _list.AddRange(tableList);            
553         }
554
555         // Return value:
556         //      >= 0: find the match
557         //        -1: No match
558         //        -2: At least two matches with different cases
559         //        -3: At least two matches with different namespaces
560         internal int InternalIndexOf(string tableName) {
561             int cachedI = -1;
562             if ((null != tableName) && (0 < tableName.Length)) {
563                 int count = _list.Count;
564                 int result = 0;
565                 for (int i = 0; i < count; i++) {
566                     DataTable table = (DataTable) _list[i];
567                     result = NamesEqual(table.TableName, tableName, false, dataSet.Locale);
568                     if (result == 1) {
569                         // ok, we have found a table with the same name.
570                         // let's see if there are any others with the same name
571                         // if any let's return (-3) otherwise...
572                         for (int j=i+1;j<count;j++) {
573                             DataTable dupTable = (DataTable) _list[j];
574                             if (NamesEqual(dupTable.TableName, tableName, false, dataSet.Locale) == 1)
575                                 return -3;
576                         }
577                        //... let's just return i
578                         return i;
579                     }
580
581                     if (result == -1)
582                         cachedI = (cachedI == -1) ? i : -2;
583                 }
584             }
585             return cachedI;
586         }
587
588         // Return value:
589         //      >= 0: find the match
590         //        -1: No match
591         //        -2: At least two matches with different cases
592         internal int InternalIndexOf(string tableName, string tableNamespace) {
593             int cachedI = -1;
594             if ((null != tableName) && (0 < tableName.Length)) {
595                 int count = _list.Count;
596                 int result = 0;
597                 for (int i = 0; i < count; i++) {
598                     DataTable table = (DataTable) _list[i];
599                     result = NamesEqual(table.TableName, tableName, false, dataSet.Locale);
600                     if ((result == 1) && (table.Namespace == tableNamespace))
601                         return i;
602
603                     if ((result == -1)  && (table.Namespace == tableNamespace))
604                         cachedI = (cachedI == -1) ? i : -2;
605                 }
606             }
607             return cachedI;
608
609         }
610
611         internal void FinishInitCollection() {
612             if (delayedAddRangeTables != null) {
613                 foreach(DataTable table in delayedAddRangeTables) {
614                     if (table != null) {
615                         Add(table);
616                     }
617                 }
618                 delayedAddRangeTables = null;
619             }
620         }
621
622         /// <devdoc>
623         /// Makes a default name with the given index.  e.g. Table1, Table2, ... Tablei
624         /// </devdoc>
625         private string MakeName(int index) {
626             if (1 == index) {
627                 return "Table1";
628             }
629             return "Table" + index.ToString(System.Globalization.CultureInfo.InvariantCulture);
630         }
631
632         /// <devdoc>
633         ///    <para>
634         ///       Raises the <see cref='System.Data.DataTableCollection.OnCollectionChanged'/> event.
635         ///    </para>
636         /// </devdoc>
637         private void OnCollectionChanged(CollectionChangeEventArgs ccevent) {
638             if (onCollectionChangedDelegate != null) {
639                 Bid.Trace("<ds.DataTableCollection.OnCollectionChanged|INFO> %d#\n", ObjectID);
640                 onCollectionChangedDelegate(this, ccevent);
641             }
642         }
643
644         /// <devdoc>
645         ///    <para>[To be supplied.]</para>
646         /// </devdoc>
647         private void OnCollectionChanging(CollectionChangeEventArgs ccevent) {
648             if (onCollectionChangingDelegate != null) {
649                 Bid.Trace("<ds.DataTableCollection.OnCollectionChanging|INFO> %d#\n", ObjectID);
650                 onCollectionChangingDelegate(this, ccevent);
651             }
652         }
653
654         /// <devdoc>
655         /// Registers this name as being used in the collection.  Will throw an ArgumentException
656         /// if the name is already being used.  Called by Add, All property, and Table.TableName property.
657         /// if the name is equivalent to the next default name to hand out, we increment our defaultNameIndex.
658         /// </devdoc>
659         internal void RegisterName(string name, string tbNamespace) {
660             Bid.Trace("<ds.DataTableCollection.RegisterName|INFO> %d#, name='%ls', tbNamespace='%ls'\n", ObjectID, name, tbNamespace);
661             Debug.Assert (name != null);
662
663             CultureInfo locale = dataSet.Locale;
664             int tableCount = _list.Count;
665             for (int i = 0; i < tableCount; i++) {
666                 DataTable table = (DataTable) _list[i];
667                 if (NamesEqual(name, table.TableName, true, locale) != 0 && (tbNamespace == table.Namespace)) {
668                     throw ExceptionBuilder.DuplicateTableName(((DataTable) _list[i]).TableName);
669                 }
670             }
671             if (NamesEqual(name, MakeName(defaultNameIndex), true, locale) != 0) {
672                 defaultNameIndex++;
673             }
674         }
675
676         /// <devdoc>
677         ///    <para>
678         ///       Removes the specified table from the collection.
679         ///    </para>
680         /// </devdoc>
681         public void Remove(DataTable table) {
682             IntPtr hscp;
683             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.Remove|API> %d#, table=%d\n", ObjectID, (table != null) ? table.ObjectID : 0);
684             try {
685                 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Remove, table));
686                 BaseRemove(table);
687                 OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, table));
688             }
689             finally{
690                 Bid.ScopeLeave(ref hscp);
691             }
692         }
693
694         /// <devdoc>
695         ///    <para>
696         ///       Removes the
697         ///       table at the given index from the collection
698         ///    </para>
699         /// </devdoc>
700         public void RemoveAt(int index) {
701             IntPtr hscp;
702             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.RemoveAt|API> %d#, index=%d\n", ObjectID, index);
703             try {
704                 DataTable dt = this[index];
705                 if (dt == null)
706                     throw ExceptionBuilder.TableOutOfRange(index);
707                 Remove(dt);
708             }
709             finally {
710                 Bid.ScopeLeave(ref hscp);
711             }
712         }
713
714         /// <devdoc>
715         ///    <para>
716         ///       Removes the table with a specified name from the
717         ///       collection.
718         ///    </para>
719         /// </devdoc>
720         public void Remove(string name) {
721             IntPtr hscp;
722             Bid.ScopeEnter(out hscp, "<ds.DataTableCollection.Remove|API> %d#, name='%ls'\n", ObjectID, name);
723             try {
724                 DataTable dt = this[name];
725                 if (dt == null)
726                     throw ExceptionBuilder.TableNotInTheDataSet(name);
727                 Remove(dt);
728             }
729             finally{
730                 Bid.ScopeLeave(ref hscp);
731             }
732         }
733
734         public void Remove(string name, string tableNamespace) {
735             if (name == null)
736                 throw ExceptionBuilder.ArgumentNull("name");
737             if (tableNamespace == null)
738                 throw ExceptionBuilder.ArgumentNull("tableNamespace");
739             DataTable dt = this[name, tableNamespace];
740             if (dt == null)
741                 throw ExceptionBuilder.TableNotInTheDataSet(name);
742             Remove(dt);
743         }
744
745
746         /// <devdoc>
747         /// Unregisters this name as no longer being used in the collection.  Called by Remove, All property, and
748         /// Table.TableName property.  If the name is equivalent to the last proposed default name, we walk backwards
749         /// to find the next proper default name to  use.
750         /// </devdoc>
751         internal void UnregisterName(string name) {
752             Bid.Trace("<ds.DataTableCollection.UnregisterName|INFO> %d#, name='%ls'\n", ObjectID, name);
753             if (NamesEqual(name, MakeName(defaultNameIndex - 1), true, dataSet.Locale) != 0) {
754                 do {
755                     defaultNameIndex--;
756                 } while (defaultNameIndex > 1 &&
757                          !Contains(MakeName(defaultNameIndex - 1)));
758             }
759         }
760     }
761 }