2006-03-05 Senganal T <tsenganal@novell.com>
[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                 [MonoTODO]
88                 public DataTable this [string name, string tbNamespace] {
89                         get { throw new NotImplementedException (); }
90                 }
91 #endif
92
93                 protected override ArrayList List {
94                         get { return base.List; }
95                 }
96
97                 #endregion
98         
99                 #region Methods 
100
101                 public
102 #if !NET_2_0
103                 virtual
104 #endif
105                 DataTable Add () 
106                 {
107                         DataTable Table = new DataTable ();
108                         Add (Table);
109                         return Table;
110                 }
111
112                 public
113 #if !NET_2_0
114                 virtual
115 #endif
116                 void Add (DataTable table) 
117                 {
118                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
119                         // check if the reference is a null reference
120                         if(table == null)
121                                 throw new ArgumentNullException("table");
122             
123                         // check if the list already contains this tabe.
124                         if(List.Contains(table))
125                                 throw new ArgumentException("DataTable already belongs to this DataSet.");
126                         
127                         // check if table is part of another DataSet 
128                         if (table.DataSet != null && table.DataSet != this.dataSet)
129                                 throw new ArgumentException ("DataTable already belongs to another DataSet");
130
131                         // if the table name is null or empty string.
132                         // give her a name. 
133                         if (table.TableName == null || table.TableName == string.Empty)
134                                 NameTable (table);
135                     
136                         // check if the collection has a table with the same name.
137                         int tmp = IndexOf(table.TableName);
138                         // if we found a table with same name we have to check
139                         // that it is the same case.
140                         // indexof can return a table with different case letters.
141                         if (tmp != -1)
142                         {
143                                 if(table.TableName == this[tmp].TableName)
144                                         throw new DuplicateNameException("A DataTable named '" + table.TableName + "' already belongs to this DataSet.");
145                         }
146
147                         List.Add (table);
148                         table.dataSet = dataSet;
149                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
150                 }
151
152                 public
153 #if !NET_2_0
154                 virtual
155 #endif
156                 DataTable Add (string name) 
157                 {
158                         DataTable table = new DataTable (name);
159                         this.Add (table);
160                         return table;
161                 }
162
163 #if NET_2_0
164                 public DataTable Add (string name, string tbNamespace)
165                 {
166                         DataTable table = new DataTable (name, tbNamespace);
167                         this.Add (table);
168                         return table;
169                 }
170 #endif
171
172                 public void AddRange (DataTable[] tables)
173                 {
174                         if (dataSet != null && dataSet.InitInProgress) {
175                                 mostRecentTables = tables;
176                                 return;
177                         }
178
179                         if (tables == null)
180                                 return;
181
182                         foreach (DataTable table in tables) {
183                                 if (table == null)
184                                         continue;
185                                 Add (table);
186                         }
187                 }
188
189                 internal void PostAddRange ()
190                 {
191                         if (mostRecentTables == null)
192                                 return;
193
194                         foreach (DataTable table in mostRecentTables){
195                                 if (table == null)
196                                         continue;
197                                 Add (table);
198                         }
199                         mostRecentTables = null;
200                 }
201
202                 public bool CanRemove (DataTable table) 
203                 {
204                         return CanRemove(table, false);
205                 }
206
207                 public void Clear () 
208                 {
209                         List.Clear ();
210                 }
211
212                 public bool Contains (string name) 
213                 {
214                         return (-1 != IndexOf (name, false));
215                 }
216
217                 public
218 #if !NET_2_0
219                 virtual
220 #endif
221                 int IndexOf (DataTable table) 
222                 {
223                         return List.IndexOf (table);
224                 }
225
226                 public
227 #if !NET_2_0
228                 virtual
229 #endif
230                 int IndexOf (string name) 
231                 {
232                         return IndexOf (name, false);
233                 }
234
235                 public void Remove (DataTable table) 
236                 {
237                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
238                         if (CanRemove(table, true))
239                                 table.dataSet = null;
240
241                         List.Remove(table);
242                         table.dataSet = null;
243                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
244                 }
245
246                 public void Remove (string name) 
247                 {
248                         if ( IndexOf (name, false) == -1)
249                                 throw new ArgumentException ("Table " + name + " does not belong to this DataSet"); 
250                         Remove (this [name]);
251                 }
252
253                 public void RemoveAt (int index) 
254                 {
255                         Remove(this[index]);
256                 }
257
258                 #endregion
259
260                 #region Protected methods
261
262                 protected internal
263 #if !NET_2_0
264                 virtual
265 #endif
266                 void OnCollectionChanging (CollectionChangeEventArgs Args)
267                 {
268                         if (CollectionChanging != null)
269                                 CollectionChanging (this, Args);
270                 }
271
272                 protected
273 #if !NET_2_0
274                 virtual
275 #endif
276                 void OnCollectionChanged (CollectionChangeEventArgs Args)
277                 {
278                         if (CollectionChanged != null)
279                                 CollectionChanged (this, Args);
280                 }
281
282                 #endregion
283
284                 #region Private methods
285
286                 private int IndexOf (string name, bool error)
287                 {
288                         int count = 0, match = -1;
289                         for (int i = 0; i < List.Count; i++)
290                         {
291                                 String name2 = ((DataTable) List[i]).TableName;
292                                 if (String.Compare (name, name2, true) == 0)
293                                 {
294                                         if (String.Compare (name, name2, false) == 0)
295                                                 return i;
296                                         match = i;
297                                         count++;
298                                 }
299                         }
300                         if (count == 1)
301                                 return match;
302                         if (count > 1 && error)
303                                 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
304                         return -1;
305                 }
306
307                 /// <summary>
308                 /// gives name to Table (Table1, Table2, Table3,...)
309                 /// </summary>
310                 private void NameTable (DataTable Table)
311                 {
312                         string Name = "Table";
313                         int i = 1;
314                         while (Contains (Name + i))
315                                 i++;
316
317                         Table.TableName = Name + i;                            
318                 }
319                 
320                 // check if a table can be removed from this collectiuon.
321                 // if the table can not be remved act according to throwException parameter.
322                 // if it is true throws an Exception, else return false.
323                 private bool CanRemove(DataTable table, bool throwException)
324                 {
325
326                         // check if table is null reference
327                         if (table == null)
328                         {
329                                 if(throwException)
330                                         throw new ArgumentNullException("table");
331                                 return false;
332                         }
333
334                         // check if the table doesnot belong to this collection
335                         bool tableexists = true;
336                         int tmp = IndexOf(table.TableName);
337                         // if we found a table with same name we have to check
338                         // that it is the same case.
339                         // indexof can return a table with different case letters.
340                         if (tmp != -1)
341                         {
342                                  if(table.TableName != this[tmp].TableName)
343                                         tableexists = false;
344                         }
345                         else
346                                 tableexists = false;
347
348                         if (!tableexists)
349                         {
350                                 if(throwException)
351                                         throw new ArgumentException("Table does not exist in collection");
352                                 return false;
353
354                         }               
355                                 
356                                                 
357                         // check if the table has the same DataSet as this collection.
358                         if(table.DataSet != this.dataSet)
359                         {
360                                 if(throwException)
361                                         throw new ArgumentException("Table " + table.TableName + " does not belong to this DataSet.");
362                                 return false;
363                         }
364                         
365                         // check the table has a relation attached to it.
366                         if (table.ParentRelations.Count > 0 || table.ChildRelations.Count > 0)
367                         {
368                                 if(throwException)
369                                         throw new ArgumentException("Cannot remove a table that has existing relations. Remove relations first.");
370                                 return false;
371                         }
372                         
373
374                         // now we check if any ForeignKeyConstraint is referncing 'table'.
375                         IEnumerator tableEnumerator = this.dataSet.Tables.GetEnumerator();
376                         
377                         // loop on all tables in dataset
378                         while (tableEnumerator.MoveNext())
379                         {
380                                 IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
381                                 // loop on all constrains in the current table
382                                 while (constraintEnumerator.MoveNext())
383                                 {
384                                         Object o = constraintEnumerator.Current;
385                                         // we only check ForeignKeyConstraint
386                                         if (o is ForeignKeyConstraint)
387                                         {
388                                                 ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
389                                                 if(fc.Table == table || fc.RelatedTable == table)
390                                                 {
391                                                         if(throwException)
392                                                                 throw new ArgumentException("Cannot remove table " + table.TableName + ", because it referenced in ForeignKeyConstraint " + fc.ConstraintName + ". Remove the constraint first.");
393                                                         return false;
394                                                 }
395                                         }
396                                 }
397                         }
398
399                         return true;
400                 }
401
402                 #endregion // Private methods
403
404                 #region Events
405
406                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]             
407                 public event CollectionChangeEventHandler CollectionChanged;
408
409                 public event CollectionChangeEventHandler CollectionChanging;
410
411                 #endregion
412         }
413 }