Merge pull request #600 from tr8dr/master
[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 using System.IO;
40 using System.Runtime.Serialization;
41 using System.Runtime.Serialization.Formatters.Binary;
42
43 namespace System.Data
44 {
45         /// <summary>
46         /// Represents the collection of tables for the DataSet.
47         /// </summary>
48         [Editor ("Microsoft.VSDesigner.Data.Design.TablesCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
49                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
50         [DefaultEvent ("CollectionChanged")]
51         [ListBindable (false)]
52         public partial class DataTableCollection : InternalDataCollectionBase {
53                 DataSet dataSet;
54                 DataTable[] mostRecentTables;
55
56                 #region Constructors
57
58                 internal DataTableCollection (DataSet dataSet)
59                         : base ()
60                 {
61                         this.dataSet = dataSet;
62                 }
63
64                 #endregion
65
66                 #region Properties
67
68                 public DataTable this [int index] {
69                         get {
70                                 if (index < 0 || index >= List.Count)
71                                         throw new IndexOutOfRangeException (String.Format ("Cannot find table {0}", index));
72                                 return (DataTable)(List [index]);
73                         }
74                 }
75
76                 public DataTable this [string name] {
77                         get {
78                                 int index = IndexOf (name, true);
79                                 return index < 0 ? null : (DataTable) List [index];
80                         }
81                 }
82
83                 protected override ArrayList List {
84                         get { return base.List; }
85                 }
86
87                 #endregion
88
89                 #region Methods
90
91                 public
92 #if !NET_2_0
93                 virtual
94 #endif
95                 DataTable Add ()
96                 {
97                         DataTable Table = new DataTable ();
98                         Add (Table);
99                         return Table;
100                 }
101
102                 public
103 #if !NET_2_0
104                 virtual
105 #endif
106                 void Add (DataTable table)
107                 {
108                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
109                         // check if the reference is a null reference
110                         if (table == null)
111                                 throw new ArgumentNullException ("table");
112
113                         // check if the list already contains this tabe.
114                         if (List.Contains (table))
115                                 throw new ArgumentException ("DataTable already belongs to this DataSet.");
116
117                         // check if table is part of another DataSet
118                         if (table.DataSet != null && table.DataSet != this.dataSet)
119                                 throw new ArgumentException ("DataTable already belongs to another DataSet");
120
121                         // if the table name is null or empty string.
122                         // give her a name.
123                         if (table.TableName == null || table.TableName == string.Empty)
124                                 NameTable (table);
125
126                         // check if the collection has a table with the same name.
127 #if !NET_2_0
128                         int tmp = IndexOf (table.TableName);
129 #else
130                         int tmp = IndexOf (table.TableName, table.Namespace);
131 #endif
132                         // if we found a table with same name we have to check
133                         // that it is the same case.
134                         // indexof can return a table with different case letters.
135                         if (tmp != -1 &&
136                             table.TableName == this [tmp].TableName)
137                                 throw new DuplicateNameException ("A DataTable named '" + table.TableName + "' already belongs to this DataSet.");
138
139                         List.Add (table);
140                         table.dataSet = dataSet;
141                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, table));
142                 }
143
144                 public
145 #if !NET_2_0
146                 virtual
147 #endif
148                 DataTable Add (string name)
149                 {
150                         DataTable table = new DataTable (name);
151                         this.Add (table);
152                         return table;
153                 }
154
155                 public void AddRange (DataTable [] tables)
156                 {
157                         if (dataSet != null && dataSet.InitInProgress) {
158                                 mostRecentTables = tables;
159                                 return;
160                         }
161
162                         if (tables == null)
163                                 return;
164
165                         foreach (DataTable table in tables) {
166                                 if (table == null)
167                                         continue;
168                                 Add (table);
169                         }
170                 }
171
172                 internal void PostAddRange ()
173                 {
174                         if (mostRecentTables == null)
175                                 return;
176
177                         foreach (DataTable table in mostRecentTables){
178                                 if (table == null)
179                                         continue;
180                                 Add (table);
181                         }
182                         mostRecentTables = null;
183                 }
184
185                 public bool CanRemove (DataTable table)
186                 {
187                         return CanRemove (table, false);
188                 }
189
190                 public void Clear ()
191                 {
192                         List.Clear ();
193                 }
194
195                 public bool Contains (string name)
196                 {
197                         return (-1 != IndexOf (name, false));
198                 }
199
200                 public
201 #if !NET_2_0
202                 virtual
203 #endif
204                 int IndexOf (DataTable table)
205                 {
206                         return List.IndexOf (table);
207                 }
208
209                 public
210 #if !NET_2_0
211                 virtual
212 #endif
213                 int IndexOf (string tableName)
214                 {
215                         return IndexOf (tableName, false);
216                 }
217
218                 public void Remove (DataTable table)
219                 {
220                         OnCollectionChanging (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
221                         if (CanRemove (table, true))
222                                 table.dataSet = null;
223
224                         List.Remove (table);
225                         table.dataSet = null;
226                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, table));
227                 }
228
229                 public void Remove (string name)
230                 {
231                         int index = IndexOf (name, false);
232                         if (index == -1)
233                                 throw new ArgumentException ("Table " + name + " does not belong to this DataSet");
234                         RemoveAt (index);
235                 }
236
237                 public void RemoveAt (int index)
238                 {
239                         Remove (this [index]);
240                 }
241
242                 #endregion
243
244                 #region Protected methods
245
246 #if !NET_2_0
247                 protected internal virtual
248 #else
249                 internal
250 #endif
251                 void OnCollectionChanging (CollectionChangeEventArgs ccevent)
252                 {
253                         if (CollectionChanging != null)
254                                 CollectionChanging (this, ccevent);
255                 }
256
257 #if !NET_2_0
258                 protected virtual
259 #else
260                 internal
261 #endif
262                 void OnCollectionChanged (CollectionChangeEventArgs ccevent)
263                 {
264                         if (CollectionChanged != null)
265                                 CollectionChanged (this, ccevent);
266                 }
267
268                 #endregion
269
270                 #region Private methods
271
272                 private int IndexOf (string name, bool error, int start)
273                 {
274                         int count = 0, match = -1;
275                         for (int i = start; i < List.Count; i++) {
276                                 String name2 = ((DataTable) List[i]).TableName;
277                                 if (String.Compare (name, name2, false, dataSet.Locale) == 0)
278                                         return i;
279                                 if (String.Compare (name, name2, true, dataSet.Locale) == 0) {
280                                         match = i;
281                                         count++;
282                                 }
283                         }
284                         if (count == 1)
285                                 return match;
286                         if (count > 1 && error)
287                                 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
288                         return -1;
289                 }
290
291                 /// <summary>
292                 /// gives name to Table (Table1, Table2, Table3,...)
293                 /// </summary>
294                 private void NameTable (DataTable Table)
295                 {
296                         string Name = "Table";
297                         int i = 1;
298                         while (Contains (Name + i))
299                                 i++;
300
301                         Table.TableName = Name + i;
302                 }
303
304                 // check if a table can be removed from this collectiuon.
305                 // if the table can not be remved act according to throwException parameter.
306                 // if it is true throws an Exception, else return false.
307                 private bool CanRemove (DataTable table, bool throwException)
308                 {
309                         // check if table is null reference
310                         if (table == null) {
311                                 if (throwException)
312                                         throw new ArgumentNullException ("table");
313                                 return false;
314                         }
315
316                         // check if the table has the same DataSet as this collection.
317                         if (table.DataSet != this.dataSet) {
318                                 if (!throwException)
319                                         return false;
320                                 throw new ArgumentException ("Table " + table.TableName + " does not belong to this DataSet.");
321                         }
322
323                         // check the table has a relation attached to it.
324                         if (table.ParentRelations.Count > 0 || table.ChildRelations.Count > 0) {
325                                 if (!throwException)
326                                         return false;
327                                 throw new ArgumentException ("Cannot remove a table that has existing relations. Remove relations first.");
328                         }
329
330                         // now we check if any ForeignKeyConstraint is referncing 'table'.
331                         foreach (Constraint c in table.Constraints) {
332                                 UniqueConstraint uc = c as UniqueConstraint;
333                                 if (uc != null) {
334                                         if (uc.ChildConstraint == null)
335                                                 continue;
336
337                                         if (!throwException)
338                                                 return false;
339                                         RaiseForeignKeyReferenceException (table.TableName, uc.ChildConstraint.ConstraintName);
340                                 }
341
342                                 ForeignKeyConstraint fc = c as ForeignKeyConstraint;
343                                 if (fc == null)
344                                         continue;
345
346                                 if (!throwException)
347                                         return false;
348                                 RaiseForeignKeyReferenceException (table.TableName, fc.ConstraintName);
349                         }
350
351                         return true;
352                 }
353
354                 private void RaiseForeignKeyReferenceException (string table, string constraint)
355                 {
356                         throw new ArgumentException (String.Format ("Cannot remove table {0}, because it is referenced" +
357                                                                 " in ForeignKeyConstraint {1}. Remove the constraint first.",
358                                                                 table, constraint));
359                 }
360
361                 #endregion // Private methods
362
363                 #region Events
364
365                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
366                 public event CollectionChangeEventHandler CollectionChanged;
367
368                 public event CollectionChangeEventHandler CollectionChanging;
369
370                 #endregion
371         }
372
373 #if NET_2_0
374         sealed partial class DataTableCollection {
375                 public DataTable this [string name, string tableNamespace] {
376                         get {
377                                 int index = IndexOf (name, tableNamespace, true);
378                                 return index < 0 ? null : (DataTable) List [index];
379                         }
380                 }
381
382                 public DataTable Add (string name, string tableNamespace)
383                 {
384                         DataTable table = new DataTable (name, tableNamespace);
385                         this.Add (table);
386                         return table;
387                 }
388
389                 public bool Contains (string name, string tableNamespace)
390                 {
391                         return (IndexOf (name, tableNamespace) != -1);
392                 }
393
394                 public int IndexOf (string tableName, string tableNamespace)
395                 {
396                         if (tableNamespace == null)
397                                 throw new ArgumentNullException ("'tableNamespace' argument cannot be null.",
398                                                 "tableNamespace");
399                         return IndexOf (tableName, tableNamespace, false);
400                 }
401
402                 public void Remove (string name, string tableNamespace)
403                 {
404                         int index = IndexOf (name, tableNamespace, true);
405                         if (index == -1)
406                                  throw new ArgumentException ("Table " + name + " does not belong to this DataSet");
407
408                         RemoveAt (index);
409                 }
410
411                 private int IndexOf (string name, string ns, bool error)
412                 {
413                         int index = -1, count = 0, match = -1;
414                         do {
415                                 index = IndexOf (name, error, index+1);
416
417                                 if (index == -1)
418                                         break;
419
420                                 if (ns == null) {
421                                         if (count > 1)
422                                                 break;
423                                         count++;
424                                         match = index;
425                                 } else if (this [index].Namespace.Equals (ns))
426                                         return index;
427
428                         } while (index != -1 && index < Count);
429
430                         if (count == 1)
431                                 return match;
432
433                         if (count == 0 || !error)
434                                 return -1;
435
436                         throw new ArgumentException ("The given name '" + name + "' matches atleast two names" +
437                                         "in the collection object with different namespaces");
438                 }
439
440                 private int IndexOf (string name, bool error)
441                 {
442                         return IndexOf (name, null, error);
443                 }
444
445                 public void CopyTo (DataTable [] array, int index)
446                 {
447                         CopyTo ((Array) array, index);
448                 }
449
450                 internal void BinarySerialize_Schema (SerializationInfo si)
451                 {
452                         si.AddValue ("DataSet.Tables.Count", Count);
453                         for (int i = 0; i < Count; i++) {
454                                 DataTable dt = (DataTable) List [i];
455
456                                 if (dt.dataSet != dataSet)
457                                         throw new SystemException ("Internal Error: inconsistent DataTable");
458
459                                 MemoryStream ms = new MemoryStream ();
460                                 BinaryFormatter bf = new BinaryFormatter ();
461                                 bf.Serialize (ms, dt);
462                                 byte [] serializedStream = ms.ToArray ();
463                                 ms.Close ();
464                                 si.AddValue ("DataSet.Tables_" + i, serializedStream, typeof (Byte []));
465                         }
466                 }
467
468                 internal void BinarySerialize_Data (SerializationInfo si)
469                 {
470                         for (int i = 0; i < Count; i++) {
471                                 DataTable dt = (DataTable) List [i];
472                                 for (int j = 0; j < dt.Columns.Count; j++) {
473                                         si.AddValue ("DataTable_" + i + ".DataColumn_" + j + ".Expression",
474                                                      dt.Columns[j].Expression);
475                                 }
476                                 dt.BinarySerialize (si, "DataTable_" + i + ".");
477                         }
478                 }
479         }
480 #else
481         [Serializable]
482         partial class DataTableCollection {
483                 private int IndexOf (string name, bool error)
484                 {
485                         return IndexOf (name, error, 0);
486                 }
487         }
488 #endif
489 }