importing messaging-2008 branch to trunk [continued]
[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 {
42         /// <summary>
43         /// Represents the collection of tables for the DataSet.
44         /// </summary>
45         [Editor ("Microsoft.VSDesigner.Data.Design.TablesCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
46                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
47         [DefaultEvent ("CollectionChanged")]
48         [ListBindable (false)]
49 #if !NET_2_0
50         [Serializable]
51 #endif
52         public
53 #if NET_2_0
54         sealed
55 #endif
56         class DataTableCollection : InternalDataCollectionBase {
57                 DataSet dataSet;
58                 DataTable[] mostRecentTables;
59
60                 #region Constructors
61
62                 internal DataTableCollection (DataSet dataSet)
63                         : base ()
64                 {
65                         this.dataSet = dataSet;
66                 }
67
68                 #endregion
69
70                 #region Properties
71
72                 public DataTable this [int index] {
73                         get {
74                                 if (index < 0 || index >= List.Count)
75                                         throw new IndexOutOfRangeException (String.Format ("Cannot find table {0}", index));
76                                 return (DataTable)(List [index]);
77                         }
78                 }
79
80                 public DataTable this [string name] {
81                         get {
82                                 int index = IndexOf (name, true);
83                                 return index < 0 ? null : (DataTable) List [index];
84                         }
85                 }
86
87 #if NET_2_0
88                 public DataTable this [string name, string tbNamespace] {
89                         get {
90                                 int index = IndexOf (name, tbNamespace, true);
91                                 return index < 0 ? null : (DataTable) List [index];
92                         }
93                 }
94 #endif
95
96                 protected override ArrayList List {
97                         get { return base.List; }
98                 }
99
100                 #endregion
101
102                 #region Methods
103
104                 public
105 #if !NET_2_0
106                 virtual
107 #endif
108                 DataTable Add ()
109                 {
110                         DataTable Table = new DataTable ();
111                         Add (Table);
112                         return Table;
113                 }
114
115                 public
116 #if !NET_2_0
117                 virtual
118 #endif
119                 void Add (DataTable table)
120                 {
121                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
122                         // check if the reference is a null reference
123                         if (table == null)
124                                 throw new ArgumentNullException ("table");
125
126                         // check if the list already contains this tabe.
127                         if (List.Contains (table))
128                                 throw new ArgumentException ("DataTable already belongs to this DataSet.");
129
130                         // check if table is part of another DataSet
131                         if (table.DataSet != null && table.DataSet != this.dataSet)
132                                 throw new ArgumentException ("DataTable already belongs to another DataSet");
133
134                         // if the table name is null or empty string.
135                         // give her a name.
136                         if (table.TableName == null || table.TableName == string.Empty)
137                                 NameTable (table);
138
139                         // check if the collection has a table with the same name.
140 #if !NET_2_0
141                         int tmp = IndexOf (table.TableName);
142 #else
143                         int tmp = IndexOf (table.TableName, table.Namespace);
144 #endif
145                         // if we found a table with same name we have to check
146                         // that it is the same case.
147                         // indexof can return a table with different case letters.
148                         if (tmp != -1 &&
149                             table.TableName == this [tmp].TableName)
150                                 throw new DuplicateNameException ("A DataTable named '" + table.TableName + "' already belongs to this DataSet.");
151
152                         List.Add (table);
153                         table.dataSet = dataSet;
154                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
155                 }
156
157                 public
158 #if !NET_2_0
159                 virtual
160 #endif
161                 DataTable Add (string name)
162                 {
163                         DataTable table = new DataTable (name);
164                         this.Add (table);
165                         return table;
166                 }
167
168 #if NET_2_0
169                 public DataTable Add (string name, string tbNamespace)
170                 {
171                         DataTable table = new DataTable (name, tbNamespace);
172                         this.Add (table);
173                         return table;
174                 }
175 #endif
176
177                 public void AddRange (DataTable [] tables)
178                 {
179                         if (dataSet != null && dataSet.InitInProgress) {
180                                 mostRecentTables = tables;
181                                 return;
182                         }
183
184                         if (tables == null)
185                                 return;
186
187                         foreach (DataTable table in tables) {
188                                 if (table == null)
189                                         continue;
190                                 Add (table);
191                         }
192                 }
193
194                 internal void PostAddRange ()
195                 {
196                         if (mostRecentTables == null)
197                                 return;
198
199                         foreach (DataTable table in mostRecentTables){
200                                 if (table == null)
201                                         continue;
202                                 Add (table);
203                         }
204                         mostRecentTables = null;
205                 }
206
207                 public bool CanRemove (DataTable table)
208                 {
209                         return CanRemove (table, false);
210                 }
211
212                 public void Clear ()
213                 {
214                         List.Clear ();
215                 }
216
217                 public bool Contains (string name)
218                 {
219                         return (-1 != IndexOf (name, false));
220                 }
221
222 #if NET_2_0
223                 public bool Contains (string name, string tableNamespace)
224                 {
225                         return (IndexOf (name, tableNamespace) != -1);
226                 }
227
228                 public void CopyTo (DataTable [] array, int index)
229                 {
230                         CopyTo ((Array) array, index);
231                 }
232 #endif
233
234                 public
235 #if !NET_2_0
236                 virtual
237 #endif
238                 int IndexOf (DataTable table)
239                 {
240                         return List.IndexOf (table);
241                 }
242
243                 public
244 #if !NET_2_0
245                 virtual
246 #endif
247                 int IndexOf (string tableName)
248                 {
249                         return IndexOf (tableName, false);
250                 }
251
252 #if NET_2_0
253                 public int IndexOf (string tableName, string tableNamespace)
254                 {
255                         if (tableNamespace == null)
256                                 throw new ArgumentNullException ("'tableNamespace' argument cannot be null.",
257                                                 "tableNamespace");
258                         return IndexOf (tableName, tableNamespace, false);
259                 }
260 #endif
261
262                 public void Remove (DataTable table)
263                 {
264                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
265                         if (CanRemove (table, true))
266                                 table.dataSet = null;
267
268                         List.Remove (table);
269                         table.dataSet = null;
270                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
271                 }
272
273                 public void Remove (string name)
274                 {
275                         int index = IndexOf (name, false);
276                         if (index == -1)
277                                 throw new ArgumentException ("Table " + name + " does not belong to this DataSet");
278                         RemoveAt (index);
279                 }
280
281 #if NET_2_0
282                 public void Remove (string name, string tableNamespace)
283                 {
284                         int index = IndexOf (name, tableNamespace, true);
285                         if (index == -1)
286                                  throw new ArgumentException ("Table " + name + " does not belong to this DataSet");
287
288                         RemoveAt (index);
289                 }
290 #endif
291
292                 public void RemoveAt (int index)
293                 {
294                         Remove (this [index]);
295                 }
296
297                 #endregion
298
299                 #region Protected methods
300
301 #if !NET_2_0
302                 protected internal virtual
303 #else
304                 internal
305 #endif
306                 void OnCollectionChanging (CollectionChangeEventArgs ccevent)
307                 {
308                         if (CollectionChanging != null)
309                                 CollectionChanging (this, ccevent);
310                 }
311
312 #if !NET_2_0
313                 protected virtual
314 #else
315                 internal
316 #endif
317                 void OnCollectionChanged (CollectionChangeEventArgs ccevent)
318                 {
319                         if (CollectionChanged != null)
320                                 CollectionChanged (this, ccevent);
321                 }
322
323                 #endregion
324
325                 #region Private methods
326 #if NET_2_0
327                 private int IndexOf (string name, string ns, bool error)
328                 {
329                         int index = -1, count = 0, match = -1;
330                         do {
331                                 index = IndexOf (name, error, index+1);
332
333                                 if (index == -1)
334                                         break;
335
336                                 if (ns == null) {
337                                         if (count > 1)
338                                                 break;
339                                         count++;
340                                         match = index;
341                                 } else if (this [index].Namespace.Equals (ns))
342                                         return index;
343
344                         } while (index != -1 && index < Count);
345
346                         if (count == 1)
347                                 return match;
348
349                         if (count == 0 || !error)
350                                 return -1;
351
352                         throw new ArgumentException ("The given name '" + name + "' matches atleast two names" +
353                                         "in the collection object with different namespaces");
354                 }
355 #endif
356
357                 private int IndexOf (string name, bool error)
358                 {
359 #if NET_2_0
360                         return IndexOf (name, null, error);
361 # else
362                         return IndexOf (name, error, 0);
363 #endif
364                 }
365
366                 private int IndexOf (string name, bool error, int start)
367                 {
368                         int count = 0, match = -1;
369                         for (int i = start; i < List.Count; i++) {
370                                 String name2 = ((DataTable) List[i]).TableName;
371                                 if (String.Compare (name, name2, false) == 0)
372                                         return i;
373                                 if (String.Compare (name, name2, true) == 0) {
374                                         match = i;
375                                         count++;
376                                 }
377                         }
378                         if (count == 1)
379                                 return match;
380                         if (count > 1 && error)
381                                 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
382                         return -1;
383                 }
384
385                 /// <summary>
386                 /// gives name to Table (Table1, Table2, Table3,...)
387                 /// </summary>
388                 private void NameTable (DataTable Table)
389                 {
390                         string Name = "Table";
391                         int i = 1;
392                         while (Contains (Name + i))
393                                 i++;
394
395                         Table.TableName = Name + i;
396                 }
397
398                 // check if a table can be removed from this collectiuon.
399                 // if the table can not be remved act according to throwException parameter.
400                 // if it is true throws an Exception, else return false.
401                 private bool CanRemove (DataTable table, bool throwException)
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 }