New test.
[mono.git] / mcs / class / System.Data / System.Data / DataTableCollection.cs
1 //
2 // System.Data.DataTableCollection.cs
3 //
4 // Authors:
5 //   Christopher Podurgiel (cpodurgiel@msn.com)
6 //   Tim Coleman <tim@timcoleman.com>
7 //
8 // (C) Chris Podurgiel
9 // (C) Copyright 2002 Tim Coleman
10 //
11
12 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 using System.ComponentModel;
38 using System.Globalization;
39
40 namespace System.Data {
41         /// <summary>
42         /// Represents the collection of tables for the DataSet.
43         /// </summary>
44         [Editor ("Microsoft.VSDesigner.Data.Design.TablesCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
45                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
46         [DefaultEvent ("CollectionChanged")]
47         [ListBindable (false)]
48 #if !NET_2_0
49         [Serializable]
50 #endif
51         public
52 #if NET_2_0
53         sealed
54 #endif
55         class DataTableCollection : InternalDataCollectionBase
56         {
57                 DataSet dataSet;
58                 DataTable[] mostRecentTables;
59                 #region Constructors 
60
61                 internal DataTableCollection (DataSet dataSet)
62                         : base ()
63                 {
64                         this.dataSet = dataSet;
65                 }
66                 
67                 #endregion
68                 
69                 #region Properties
70
71                 public DataTable this[int index] {
72                         get {
73                                 if (index < 0 || index >= List.Count)
74                                         throw new IndexOutOfRangeException(String.Format("Cannot find table {0}", index));
75                                 return (DataTable)(List[index]);
76                         }
77                 }
78
79                 public DataTable this[string name] {
80                         get { 
81                                 int index = IndexOf (name, true);
82                                 return index < 0 ? null : (DataTable) List[index];
83                         }
84                 }
85
86 #if NET_2_0
87                 public DataTable this [string name, string tbNamespace] {
88                         get { 
89                                 int index = IndexOf (name, tbNamespace, true);
90                                 return index < 0 ? null : (DataTable) List[index];
91                         }
92                 }
93 #endif
94
95                 protected override ArrayList List {
96                         get { return base.List; }
97                 }
98
99                 #endregion
100         
101                 #region Methods 
102
103                 public
104 #if !NET_2_0
105                 virtual
106 #endif
107                 DataTable Add () 
108                 {
109                         DataTable Table = new DataTable ();
110                         Add (Table);
111                         return Table;
112                 }
113
114                 public
115 #if !NET_2_0
116                 virtual
117 #endif
118                 void Add (DataTable table) 
119                 {
120                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
121                         // check if the reference is a null reference
122                         if(table == null)
123                                 throw new ArgumentNullException("table");
124             
125                         // check if the list already contains this tabe.
126                         if(List.Contains(table))
127                                 throw new ArgumentException("DataTable already belongs to this DataSet.");
128                         
129                         // check if table is part of another DataSet 
130                         if (table.DataSet != null && table.DataSet != this.dataSet)
131                                 throw new ArgumentException ("DataTable already belongs to another DataSet");
132
133                         // if the table name is null or empty string.
134                         // give her a name. 
135                         if (table.TableName == null || table.TableName == string.Empty)
136                                 NameTable (table);
137                     
138                         // check if the collection has a table with the same name.
139 #if !NET_2_0
140                         int tmp = IndexOf (table.TableName);
141 #else
142                         int tmp = IndexOf (table.TableName, table.Namespace);
143 #endif
144                         // if we found a table with same name we have to check
145                         // that it is the same case.
146                         // indexof can return a table with different case letters.
147                         if (tmp != -1)
148                         {
149                                 if(table.TableName == this[tmp].TableName)
150                                         throw new DuplicateNameException("A DataTable named '" + table.TableName + "' already belongs to this DataSet.");
151                         }
152
153                         List.Add (table);
154                         table.dataSet = dataSet;
155                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
156                 }
157
158                 public
159 #if !NET_2_0
160                 virtual
161 #endif
162                 DataTable Add (string name) 
163                 {
164                         DataTable table = new DataTable (name);
165                         this.Add (table);
166                         return table;
167                 }
168
169 #if NET_2_0
170                 public DataTable Add (string name, string tbNamespace)
171                 {
172                         DataTable table = new DataTable (name, tbNamespace);
173                         this.Add (table);
174                         return table;
175                 }
176 #endif
177
178                 public void AddRange (DataTable[] tables)
179                 {
180                         if (dataSet != null && dataSet.InitInProgress) {
181                                 mostRecentTables = tables;
182                                 return;
183                         }
184
185                         if (tables == null)
186                                 return;
187
188                         foreach (DataTable table in tables) {
189                                 if (table == null)
190                                         continue;
191                                 Add (table);
192                         }
193                 }
194
195                 internal void PostAddRange ()
196                 {
197                         if (mostRecentTables == null)
198                                 return;
199
200                         foreach (DataTable table in mostRecentTables){
201                                 if (table == null)
202                                         continue;
203                                 Add (table);
204                         }
205                         mostRecentTables = null;
206                 }
207
208                 public bool CanRemove (DataTable table) 
209                 {
210                         return CanRemove(table, false);
211                 }
212
213                 public void Clear () 
214                 {
215                         List.Clear ();
216                 }
217
218                 public bool Contains (string name) 
219                 {
220                         return (-1 != IndexOf (name, false));
221                 }
222                 
223 #if NET_2_0
224                 public bool Contains (string name, string tableNamespace) 
225                 {
226                         return (IndexOf (name, tableNamespace) != -1) ;
227                 }
228 #endif
229
230                 public void CopyTo (DataTable [] array, int index)
231                 {
232                         CopyTo ((Array) array, index);
233                 }
234
235                 public
236 #if !NET_2_0
237                 virtual
238 #endif
239                 int IndexOf (DataTable table) 
240                 {
241                         return List.IndexOf (table);
242                 }
243
244                 public
245 #if !NET_2_0
246                 virtual
247 #endif
248                 int IndexOf (string name) 
249                 {
250                         return IndexOf (name, false);
251                 }
252
253 #if NET_2_0
254                 public int IndexOf (string name, string tableNamespace)
255                 {
256                         if (tableNamespace == null)
257                                 throw new ArgumentNullException ("'tableNamespace' argument cannot be null.",
258                                                 "tableNamespace");
259                         return IndexOf (name, tableNamespace, false);
260                 }
261 #endif
262                 
263                 public void Remove (DataTable table) 
264                 {
265                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
266                         if (CanRemove(table, true))
267                                 table.dataSet = null;
268
269                         List.Remove(table);
270                         table.dataSet = null;
271                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
272                 }
273
274                 public void Remove (string name) 
275                 {
276                         int index = IndexOf (name, false);
277                         if (index == -1)
278                                 throw new ArgumentException ("Table " + name + " does not belong to this DataSet"); 
279                         RemoveAt (index);
280                 }
281
282 #if NET_2_0
283                 public void Remove (string name, string tableNamespace)
284                 {
285                         int index = IndexOf (name, tableNamespace, true);
286                         if (index == -1)
287                                  throw new ArgumentException ("Table " + name + " does not belong to this DataSet");
288
289                         RemoveAt (index);
290                 }
291 #endif
292
293                 public void RemoveAt (int index) 
294                 {
295                         Remove(this[index]);
296                 }
297
298                 #endregion
299
300                 #region Protected methods
301
302                 
303 #if !NET_2_0
304                 protected internal virtual
305 #else
306                 internal
307 #endif
308                 void OnCollectionChanging (CollectionChangeEventArgs Args)
309                 {
310                         if (CollectionChanging != null)
311                                 CollectionChanging (this, Args);
312                 }
313
314                 
315 #if !NET_2_0
316                 protected virtual
317 #else
318                 internal
319 #endif
320                 void OnCollectionChanged (CollectionChangeEventArgs Args)
321                 {
322                         if (CollectionChanged != null)
323                                 CollectionChanged (this, Args);
324                 }
325
326                 #endregion
327
328                 #region Private methods
329 #if NET_2_0
330                 private int IndexOf (string name, string ns, bool error)
331                 {
332                         int index = -1, count = 0, match = -1;
333                         do {
334                                 index = IndexOf (name, error, index+1);
335
336                                 if (index == -1)
337                                         break;
338
339                                 if (ns == null) {
340                                         if (count > 1)
341                                                 break;
342                                         count++;
343                                         match = index;
344                                 } else if (this [index].Namespace.Equals (ns))
345                                         return index;
346
347                         } while (index != -1 && index < Count);
348                         
349                         if (count == 1)
350                                 return match;
351
352                         if (count == 0 || !error)
353                                 return -1;
354
355                         throw new ArgumentException ("The given name '" + name + "' matches atleast two names" +
356                                         "in the collection object with different namespaces");
357                 }
358 #endif
359
360                 private int IndexOf (string name, bool error)
361                 {
362 #if NET_2_0
363                         return IndexOf (name, null, error);
364 # else
365                         return IndexOf (name, error, 0);
366 #endif
367                 }
368
369                 private int IndexOf (string name, bool error, int start)
370                 {
371                         int count = 0, match = -1;
372                         for (int i = start; i < List.Count; i++)
373                         {
374                                 String name2 = ((DataTable) List[i]).TableName;
375                                 if (String.Compare (name, name2, false) == 0)
376                                         return i;
377                                 if (String.Compare (name, name2, true) == 0) {
378                                         match = i;
379                                         count++;
380                                 }
381                         }
382                         if (count == 1)
383                                 return match;
384                         if (count > 1 && error)
385                                 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
386                         return -1;
387                 }
388
389                 /// <summary>
390                 /// gives name to Table (Table1, Table2, Table3,...)
391                 /// </summary>
392                 private void NameTable (DataTable Table)
393                 {
394                         string Name = "Table";
395                         int i = 1;
396                         while (Contains (Name + i))
397                                 i++;
398
399                         Table.TableName = Name + i;                            
400                 }
401                 
402                 // check if a table can be removed from this collectiuon.
403                 // if the table can not be remved act according to throwException parameter.
404                 // if it is true throws an Exception, else return false.
405                 private bool CanRemove(DataTable table, bool throwException)
406                 {
407
408                         // check if table is null reference
409                         if (table == null) {
410                                 if(throwException)
411                                         throw new ArgumentNullException("table");
412                                 return false;
413                         }
414
415                         // check if the table has the same DataSet as this collection.
416                         if(table.DataSet != this.dataSet) {
417                                 if(!throwException)
418                                         return false;
419                                 throw new ArgumentException("Table " + table.TableName + " does not belong to this DataSet.");
420                         }
421                         
422                         // check the table has a relation attached to it.
423                         if (table.ParentRelations.Count > 0 || table.ChildRelations.Count > 0) {
424                                 if(!throwException)
425                                         return false;
426                                 throw new ArgumentException("Cannot remove a table that has existing relations. Remove relations first.");
427                         }
428
429                         // now we check if any ForeignKeyConstraint is referncing 'table'.
430                         foreach (Constraint c in table.Constraints) {
431                                 UniqueConstraint uc = c as UniqueConstraint;
432                                 if (uc != null) {
433                                         if (uc.ChildConstraint == null)
434                                                 continue;
435
436                                         if (!throwException)
437                                                 return false;
438                                         RaiseForeignKeyReferenceException (table.TableName, uc.ChildConstraint.ConstraintName);
439                                 }
440
441                                 ForeignKeyConstraint fc = c as ForeignKeyConstraint;
442                                 if (fc == null) 
443                                         continue;
444
445                                 if (!throwException)
446                                         return false;
447                                 RaiseForeignKeyReferenceException (table.TableName, fc.ConstraintName);
448                         }
449
450                         return true;
451                 }
452
453                 private void RaiseForeignKeyReferenceException (string table, string constraint)
454                 {
455                         throw new ArgumentException (String.Format ("Cannot remove table {0}, because it is referenced" +
456                                                                 " in ForeignKeyConstraint {1}. Remove the constraint first.",
457                                                                 table, constraint));
458                 }
459
460                 #endregion // Private methods
461
462                 #region Events
463
464                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]             
465                 public event CollectionChangeEventHandler CollectionChanged;
466
467                 public event CollectionChangeEventHandler CollectionChanging;
468
469                 #endregion
470         }
471 }