* roottypes.cs: Rename from tree.cs.
[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
231 #if !NET_2_0
232                 virtual
233 #endif
234                 int IndexOf (DataTable table) 
235                 {
236                         return List.IndexOf (table);
237                 }
238
239                 public
240 #if !NET_2_0
241                 virtual
242 #endif
243                 int IndexOf (string name) 
244                 {
245                         return IndexOf (name, false);
246                 }
247
248 #if NET_2_0
249                 public int IndexOf (string name, string tableNamespace)
250                 {
251                         if (tableNamespace == null)
252                                 throw new ArgumentNullException ("'tableNamespace' argument cannot be null.",
253                                                 "tableNamespace");
254                         return IndexOf (name, tableNamespace, false);
255                 }
256 #endif
257                 
258                 public void Remove (DataTable table) 
259                 {
260                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
261                         if (CanRemove(table, true))
262                                 table.dataSet = null;
263
264                         List.Remove(table);
265                         table.dataSet = null;
266                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
267                 }
268
269                 public void Remove (string name) 
270                 {
271                         int index = IndexOf (name, false);
272                         if (index == -1)
273                                 throw new ArgumentException ("Table " + name + " does not belong to this DataSet"); 
274                         RemoveAt (index);
275                 }
276
277 #if NET_2_0
278                 public void Remove (string name, string tableNamespace)
279                 {
280                         int index = IndexOf (name, tableNamespace, true);
281                         if (index == -1)
282                                  throw new ArgumentException ("Table " + name + " does not belong to this DataSet");
283
284                         RemoveAt (index);
285                 }
286 #endif
287
288                 public void RemoveAt (int index) 
289                 {
290                         Remove(this[index]);
291                 }
292
293                 #endregion
294
295                 #region Protected methods
296
297                 
298 #if !NET_2_0
299                 protected internal virtual
300 #else
301                 internal
302 #endif
303                 void OnCollectionChanging (CollectionChangeEventArgs Args)
304                 {
305                         if (CollectionChanging != null)
306                                 CollectionChanging (this, Args);
307                 }
308
309                 
310 #if !NET_2_0
311                 protected virtual
312 #else
313                 internal
314 #endif
315                 void OnCollectionChanged (CollectionChangeEventArgs Args)
316                 {
317                         if (CollectionChanged != null)
318                                 CollectionChanged (this, Args);
319                 }
320
321                 #endregion
322
323                 #region Private methods
324 #if NET_2_0
325                 private int IndexOf (string name, string ns, bool error)
326                 {
327                         int index = -1, count = 0, match = -1;
328                         do {
329                                 index = IndexOf (name, error, index+1);
330
331                                 if (index == -1)
332                                         break;
333
334                                 if (ns == null) {
335                                         if (count > 1)
336                                                 break;
337                                         count++;
338                                         match = index;
339                                 } else if (this [index].Namespace.Equals (ns))
340                                         return index;
341
342                         } while (index != -1 && index < Count);
343                         
344                         if (count == 1)
345                                 return match;
346
347                         if (count == 0 || !error)
348                                 return -1;
349
350                         throw new ArgumentException ("The given name '" + name + "' matches atleast two names" +
351                                         "in the collection object with different namespaces");
352                 }
353 #endif
354
355                 private int IndexOf (string name, bool error)
356                 {
357 #if NET_2_0
358                         return IndexOf (name, null, error);
359 # else
360                         return IndexOf (name, error, 0);
361 #endif
362                 }
363
364                 private int IndexOf (string name, bool error, int start)
365                 {
366                         int count = 0, match = -1;
367                         for (int i = start; i < List.Count; i++)
368                         {
369                                 String name2 = ((DataTable) List[i]).TableName;
370                                 if (String.Compare (name, name2, false) == 0)
371                                         return i;
372                                 if (String.Compare (name, name2, true) == 0) {
373                                         match = i;
374                                         count++;
375                                 }
376                         }
377                         if (count == 1)
378                                 return match;
379                         if (count > 1 && error)
380                                 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
381                         return -1;
382                 }
383
384                 /// <summary>
385                 /// gives name to Table (Table1, Table2, Table3,...)
386                 /// </summary>
387                 private void NameTable (DataTable Table)
388                 {
389                         string Name = "Table";
390                         int i = 1;
391                         while (Contains (Name + i))
392                                 i++;
393
394                         Table.TableName = Name + i;                            
395                 }
396                 
397                 // check if a table can be removed from this collectiuon.
398                 // if the table can not be remved act according to throwException parameter.
399                 // if it is true throws an Exception, else return false.
400                 private bool CanRemove(DataTable table, bool throwException)
401                 {
402
403                         // check if table is null reference
404                         if (table == null) {
405                                 if(throwException)
406                                         throw new ArgumentNullException("table");
407                                 return false;
408                         }
409
410                         // check if the table has the same DataSet as this collection.
411                         if(table.DataSet != this.dataSet) {
412                                 if(!throwException)
413                                         return false;
414                                 throw new ArgumentException("Table " + table.TableName + " does not belong to this DataSet.");
415                         }
416                         
417                         // check the table has a relation attached to it.
418                         if (table.ParentRelations.Count > 0 || table.ChildRelations.Count > 0) {
419                                 if(!throwException)
420                                         return false;
421                                 throw new ArgumentException("Cannot remove a table that has existing relations. Remove relations first.");
422                         }
423
424                         // now we check if any ForeignKeyConstraint is referncing 'table'.
425                         foreach (Constraint c in table.Constraints) {
426                                 UniqueConstraint uc = c as UniqueConstraint;
427                                 if (uc != null) {
428                                         if (uc.ChildConstraint == null)
429                                                 continue;
430
431                                         if (!throwException)
432                                                 return false;
433                                         RaiseForeignKeyReferenceException (table.TableName, uc.ChildConstraint.ConstraintName);
434                                 }
435
436                                 ForeignKeyConstraint fc = c as ForeignKeyConstraint;
437                                 if (fc == null) 
438                                         continue;
439
440                                 if (!throwException)
441                                         return false;
442                                 RaiseForeignKeyReferenceException (table.TableName, fc.ConstraintName);
443                         }
444
445                         return true;
446                 }
447
448                 private void RaiseForeignKeyReferenceException (string table, string constraint)
449                 {
450                         throw new ArgumentException (String.Format ("Cannot remove table {0}, because it is referenced" +
451                                                                 " in ForeignKeyConstraint {1}. Remove the constraint first.",
452                                                                 table, constraint));
453                 }
454
455                 #endregion // Private methods
456
457                 #region Events
458
459                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]             
460                 public event CollectionChangeEventHandler CollectionChanged;
461
462                 public event CollectionChangeEventHandler CollectionChanging;
463
464                 #endregion
465         }
466 }