importing messaging-2008 branch to trunk [continued]
[mono.git] / mcs / class / System.Data / System.Data / DataRelationCollection.cs
1 //
2 // System.Data.DataRelationCollection.cs
3 //
4 // Author:
5 //   Christopher Podurgiel (cpodurgiel@msn.com)
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Tim Coleman (tim@timcoleman.com)
8 //   Alan Tam Siu Lung <Tam@SiuLung.com>
9 //
10 // (C) Chris Podurgiel
11 // (C) 2002 Daniel Morgan
12 // Copyright (C) Tim Coleman, 2002
13 //
14
15 //
16 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 //
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
25 //
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
28 //
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 //
37
38 using System;
39 using System.Collections;
40 using System.ComponentModel;
41
42 namespace System.Data {
43         /// <summary>
44         /// Represents the collection of DataRelation objects for this DataSet.
45         /// </summary>
46         [Editor ("Microsoft.VSDesigner.Data.Design.DataRelationCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
47                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
48         [DefaultEvent ("CollectionChanged")]
49         [DefaultProperty ("Table")]
50 #if !NET_2_0
51         [Serializable]
52 #endif
53         public abstract class DataRelationCollection : InternalDataCollectionBase {
54                 /// <summary>
55                 /// Summary description for DataTableRelationCollection.
56                 /// </summary>
57                 internal class DataSetRelationCollection : DataRelationCollection {
58                         private DataSet dataSet;
59                         DataRelation [] mostRecentRelations;
60
61                         /// <summary>
62                         /// Initializes a new instance of the DataSetRelationCollection class.
63                         /// </summary>
64                         internal DataSetRelationCollection (DataSet dataSet)
65                         {
66                                 this.dataSet = dataSet;
67                         }
68
69                         protected override DataSet GetDataSet ()
70                         {
71                                 return dataSet;
72                         }
73
74                         /// <summary>
75                         /// Performs verification on the table.
76                         /// </summary>
77                         /// <param name="relation">The relation to check.</param>
78                         protected override void AddCore (DataRelation relation)
79                         {
80                                 if (relation.ChildTable.DataSet != dataSet || relation.ParentTable.DataSet != dataSet)
81                                    throw new DataException ();
82
83                                 base.AddCore (relation);
84                                 relation.ParentTable.ChildRelations.Add (relation);
85                                 relation.ChildTable.ParentRelations.Add (relation);
86                                 relation.SetDataSet (dataSet);
87                                 relation.UpdateConstraints ();
88                         }
89
90                         protected override void RemoveCore (DataRelation relation)
91                         {
92                                 base.RemoveCore (relation);
93                                 relation.SetDataSet (null);
94                                 relation.ParentTable.ChildRelations.Remove (relation);
95                                 relation.ChildTable.ParentRelations.Remove (relation);
96                                 relation.SetParentKeyConstraint (null);
97                                 relation.SetChildKeyConstraint (null);
98                         }
99
100                         public override void AddRange (DataRelation [] relations)
101                         {
102                                 if (relations == null)
103                                         return;
104
105                                 if (dataSet != null && dataSet.InitInProgress){
106                                         mostRecentRelations = relations;
107                                         return;
108                                 }
109
110                                 foreach (DataRelation rel in relations){
111                                         if (rel == null)
112                                                 continue;
113                                         Add (rel);
114                                 }
115                         }
116
117                         internal override void PostAddRange ()
118                         {
119                                 if (mostRecentRelations == null)
120                                         return;
121
122                                 foreach (DataRelation rel in mostRecentRelations){
123                                         if (rel == null)
124                                                 continue;
125                                         if (rel.InitInProgress)
126                                                 rel.FinishInit (dataSet);
127                                         Add (rel);
128                                 }
129                                 mostRecentRelations = null;
130                         }
131
132                         protected override ArrayList List {
133                                 get { return base.List; }
134                         }
135
136                         public override DataRelation this [string name] {
137                                 get {
138                                         int index = IndexOf (name, true);
139                                         return index < 0 ? null : (DataRelation) List [index];
140                                 }
141                         }
142
143                         /// <summary>
144                         /// Gets the DataRelation object at the specified index.
145                         /// </summary>
146                         public override DataRelation this [int index] {
147                                 get {
148                                         if (index < 0 || index >= List.Count)
149                                                 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}.", index));
150
151                                         return (DataRelation) List [index];
152                                 }
153                         }
154                 }
155
156                 /// <summary>
157                 /// Summary description for DataTableRelationCollection.
158                 /// </summary>
159                 internal class DataTableRelationCollection : DataRelationCollection {
160                         private DataTable dataTable;
161
162                         /// <summary>
163                         /// Initializes a new instance of the DataTableRelationCollection class.
164                         /// </summary>
165                         internal DataTableRelationCollection (DataTable dataTable)
166                         {
167                                 this.dataTable = dataTable;
168                         }
169
170                         protected override DataSet GetDataSet ()
171                         {
172                                 return dataTable.DataSet;
173                         }
174
175                         public override DataRelation this [string name] {
176                                 get {
177                                         int index = IndexOf (name, true);
178                                         return index < 0 ? null : (DataRelation) List [index];
179                                 }
180                         }
181
182                         /// <summary>
183                         /// Gets the DataRelation object at the specified index.
184                         /// </summary>
185                         public override DataRelation this [int index] {
186                                 get {
187                                         if (index < 0 || index >= List.Count)
188                                                 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}.", index));
189
190                                         return (DataRelation) List [index];
191                                 }
192                         }
193
194                         /// <summary>
195                         /// Performs verification on the table.
196                         /// </summary>
197                         /// <param name="relation">The relation to check.</param>
198                         protected override void AddCore (DataRelation relation)
199                         {
200                                 if (dataTable.ParentRelations == this && relation.ChildTable != dataTable)
201                                         throw new ArgumentException ("Cannot add a relation to this table's " +
202                                                                      "ParentRelations where this table is not" +
203                                                                      " the Child table.");
204
205                                 if (dataTable.ChildRelations == this && relation.ParentTable != dataTable)
206                                         throw new ArgumentException("Cannot add a relation to this table's " +
207                                                                     "ChildRelations where this table is not" +
208                                                                     " the Parent table.");
209
210                                 dataTable.DataSet.Relations.Add (relation);
211                                 base.AddCore (relation);
212                         }
213
214                         protected override void RemoveCore (DataRelation relation)
215                         {
216                                 relation.DataSet.Relations.Remove(relation);
217                                 base.RemoveCore (relation);
218                         }
219
220                         protected override ArrayList List {
221                                 get { return base.List; }
222                         }
223                 }
224
225                 private DataRelation inTransition;
226                 int index;
227
228
229                 /// <summary>
230                 /// Initializes a new instance of the DataRelationCollection class.
231                 /// </summary>
232                 protected DataRelationCollection ()
233                 {
234                         inTransition = null;
235                 }
236
237                 /// <summary>
238                 /// Gets the DataRelation object specified by name.
239                 /// </summary>
240                 public abstract DataRelation this [string name] {
241                         get;
242                 }
243
244                 /// <summary>
245                 /// Gets the DataRelation object at the specified index.
246                 /// </summary>
247                 public abstract DataRelation this [int index] {
248                         get;
249                 }
250
251
252                 #region Add Methods
253                 private string GetNextDefaultRelationName ()
254                 {
255                         int index = 1;
256                         string defRelationName = "Relation" + index;
257                         for (; Contains (defRelationName); ++index)
258                                 defRelationName = "Relation" + index;
259                         return defRelationName;
260                 }
261
262                 /// <summary>
263                 /// Adds a DataRelation to the DataRelationCollection.
264                 /// </summary>
265                 /// <param name="relation">The DataRelation to add to the collection.</param>
266                 public void Add (DataRelation relation)
267                 {
268                         // To prevent endless recursion
269                         if (inTransition == relation)
270                                 return;
271
272                         inTransition = relation;
273
274                         try {
275                                 CollectionChangeEventArgs e = new CollectionChangeEventArgs (CollectionChangeAction.Add, this);
276                                 OnCollectionChanging (e);
277
278                                 this.AddCore (relation);
279                                 if (relation.RelationName == string.Empty)
280                                         relation.RelationName = GenerateRelationName ();
281
282                                 relation.ParentTable.ResetPropertyDescriptorsCache ();
283                                 relation.ChildTable.ResetPropertyDescriptorsCache ();
284
285                                 e = new CollectionChangeEventArgs (CollectionChangeAction.Add, this);
286                                 OnCollectionChanged (e);
287                         } finally {
288                                 inTransition = null;
289                         }
290                 }
291
292                 private string GenerateRelationName ()
293                 {
294                         index++;
295                         return "Relation" + index;
296                 }
297
298                 /// <summary>
299                 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
300                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
301                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
302                 /// The CollectionChanged event is fired if it succeeds.
303                 /// </summary>
304                 /// <param name="parentColumn">parent column of relation.</param>
305                 /// <param name="childColumn">child column of relation.</param>
306                 /// <returns>The created DataRelation.</returns>
307                 public virtual DataRelation Add (DataColumn parentColumn, DataColumn childColumn)
308                 {
309                         DataRelation dataRelation = new DataRelation (GetNextDefaultRelationName (), parentColumn, childColumn);
310                         Add (dataRelation);
311                         return dataRelation;
312                 }
313
314                 /// <summary>
315                 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
316                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
317                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
318                 /// The CollectionChanged event is raised if it succeeds.
319                 /// </summary>
320                 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
321                 /// <param name="childColumns">An array of child DataColumn objects.</param>
322                 /// <returns>The created DataRelation.</returns>
323                 public virtual DataRelation Add (DataColumn [] parentColumns, DataColumn [] childColumns)
324                 {
325                         DataRelation dataRelation = new DataRelation (GetNextDefaultRelationName (), parentColumns, childColumns);
326                         Add (dataRelation);
327                         return dataRelation;
328                 }
329
330                 /// <summary>
331                 /// Creates a relation given the parameters and adds it to the collection.
332                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
333                 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
334                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
335                 /// The CollectionChanged event is raised if it succeeds.
336                 /// </summary>
337                 /// <param name="name">The name of the relation.</param>
338                 /// <param name="parentColumn">parent column of relation.</param>
339                 /// <returns>The created DataRelation.</returns>
340                 /// <returns></returns>
341                 public virtual DataRelation Add (string name, DataColumn parentColumn, DataColumn childColumn)
342                 {
343                         //If no name was supplied, give it a default name.
344                         if (name == null || name == "")
345                                 name = GetNextDefaultRelationName ();
346
347                         DataRelation dataRelation = new DataRelation (name, parentColumn, childColumn);
348                         Add (dataRelation);
349                         return dataRelation;
350                 }
351
352                 /// <summary>
353                 /// Creates a DataRelation with the specified name, and arrays of parent and child columns, and adds it to the collection.
354                 /// </summary>
355                 /// <param name="name">The name of the DataRelation to create.</param>
356                 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
357                 /// <param name="childColumns">An array of child DataColumn objects.</param>
358                 /// <returns>The created DataRelation.</returns>
359                 public virtual DataRelation Add (string name, DataColumn [] parentColumns, DataColumn [] childColumns)
360                 {
361                         //If no name was supplied, give it a default name.
362                         if (name == null || name == "")
363                                 name = GetNextDefaultRelationName ();
364
365                         DataRelation dataRelation = new DataRelation (name, parentColumns, childColumns);
366                         Add (dataRelation);
367                         return dataRelation;
368                 }
369
370                 /// <summary>
371                 /// Creates a relation given the parameters and adds it to the collection.
372                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
373                 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
374                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
375                 /// The CollectionChanged event is raised if it succeeds.
376                 /// </summary>
377                 /// <param name="name">The name of the relation.</param>
378                 /// <param name="parentColumn">parent column of relation.</param>
379                 /// <param name="childColumn">child column of relation.</param>
380                 /// <param name="createConstraints">true to create constraints; otherwise false. (default is true)</param>
381                 /// <returns>The created DataRelation.</returns>
382                 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
383                 {
384                         //If no name was supplied, give it a default name.
385                         if (name == null || name == "")
386                                 name = GetNextDefaultRelationName ();
387
388                         DataRelation dataRelation = new DataRelation (name, parentColumn, childColumn, createConstraints);
389                         Add (dataRelation);
390                         return dataRelation;
391                 }
392
393                 /// <summary>
394                 /// Creates a DataRelation with the specified name, arrays of parent and child columns,
395                 /// and value specifying whether to create a constraint, and adds it to the collection.
396                 /// </summary>
397                 /// <param name="name">The name of the DataRelation to create.</param>
398                 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
399                 /// <param name="childColumns">An array of child DataColumn objects.</param>
400                 /// <param name="createConstraints">true to create a constraint; otherwise false.</param>
401                 /// <returns>The created DataRelation.</returns>
402                 public virtual DataRelation Add (string name, DataColumn [] parentColumns, DataColumn [] childColumns, bool createConstraints)
403                 {
404                         //If no name was supplied, give it a default name.
405                         if (name == null || name == "")
406                                 name = GetNextDefaultRelationName ();
407
408                         DataRelation dataRelation = new DataRelation (name, parentColumns, childColumns, createConstraints);
409                         Add (dataRelation);
410                         return dataRelation;
411                 }
412                 #endregion
413
414                 /// <summary>
415                 /// Adds to the list
416                 /// </summary>
417                 /// <param name="relation">The relation to check.</param>
418                 protected virtual void AddCore (DataRelation relation)
419                 {
420                         if (relation == null)
421                                 //TODO: Issue a good exception message.
422                                 throw new ArgumentNullException();
423
424                         if(List.IndexOf (relation) != -1)
425                                 //TODO: Issue a good exception message.
426                                 throw new ArgumentException();
427
428                         // check if the collection has a relation with the same name.
429                         int tmp = IndexOf (relation.RelationName);
430                         // if we found a relation with same name we have to check
431                         // that it is the same case.
432                         // indexof can return a table with different case letters.
433                         if (tmp != -1 && relation.RelationName == this [tmp].RelationName)
434                                 throw new DuplicateNameException("A DataRelation named '" + relation.RelationName + "' already belongs to this DataSet.");
435
436                         // check whether the relation exists between the columns already
437                         foreach (DataRelation rel in this) {
438                                 // compare child columns
439                                 bool differs = false;
440                                 foreach (DataColumn current in relation.ChildColumns) {
441                                         bool exists = false;
442                                         foreach (DataColumn col in rel.ChildColumns) {
443                                                 if (col == current) {
444                                                         exists = true;
445                                                         break;
446                                                 }
447                                         }
448                                         if (!exists) {
449                                                 differs = true;
450                                                 break;
451                                         }
452                                 }
453
454                                 if (! differs) {
455                                         // compare parent columns
456                                         differs = false;
457                                         foreach (DataColumn current in relation.ParentColumns) {
458                                                 bool exists = false;
459                                                 foreach (DataColumn col in rel.ParentColumns) {
460                                                         if (col == current) {
461                                                                 exists = true;
462                                                                 break;
463                                                         }
464                                                 }
465                                                 if (!exists) {
466                                                         differs = true;
467                                                         break;
468                                                 }
469                                         }
470
471                                         if (! differs)
472                                                 throw new ArgumentException ("A relation already exists for these child columns");
473                                 }
474                         }
475
476                         // Add to collection
477                         List.Add (relation);
478                 }
479
480                 /// <summary>
481                 /// Copies the elements of the specified DataRelation array to the end of the collection.
482                 /// </summary>
483                 /// <param name="relations">The array of DataRelation objects to add to the collection.</param>
484                 public virtual void AddRange (DataRelation[] relations)
485                 {
486                         if (relations == null)
487                                 return;
488
489                         foreach (DataRelation relation in relations)
490                                 Add (relation);
491                 }
492
493 #if NET_2_0
494                 public void CopyTo (DataRelation [] array, int index)
495                 {
496                         CopyTo ((Array) array, index);
497                 }
498 #endif
499
500                 internal virtual void PostAddRange ()
501                 {
502                 }
503
504                 public virtual bool CanRemove (DataRelation relation)
505                 {
506                         if (relation == null || !GetDataSet ().Equals (relation.DataSet))
507                                 return false;
508
509                         // check if the relation doesnot belong to this collection
510                         int tmp = IndexOf (relation.RelationName);
511                         return tmp != -1 && relation.RelationName == this [tmp].RelationName;
512                 }
513
514                 public virtual void Clear ()
515                 {
516                         for (int i = 0; i < Count; i++)
517                                 Remove (this [i]);
518
519                         List.Clear ();
520                 }
521
522                 public virtual bool Contains (string name)
523                 {
524                         DataSet tmpDataSet = GetDataSet ();
525                         if (tmpDataSet != null) {
526                                 DataRelation tmpRelation = tmpDataSet.Relations [name];
527                                 if (tmpRelation != null)
528                                         return true;
529                         }
530                         return (-1 != IndexOf (name, false));
531                 }
532
533                 private CollectionChangeEventArgs CreateCollectionChangeEvent (CollectionChangeAction action)
534                 {
535                         return new CollectionChangeEventArgs (action, this);
536                 }
537
538                 protected abstract DataSet GetDataSet ();
539
540                 public virtual int IndexOf (DataRelation relation)
541                 {
542                         return List.IndexOf (relation);
543                 }
544
545                 public virtual int IndexOf (string relationName)
546                 {
547                         return IndexOf (relationName, false);
548                 }
549
550                 private int IndexOf (string name, bool error)
551                 {
552                         int count = 0, match = -1;
553                         for (int i = 0; i < List.Count; i++) {
554                                 String name2 = ((DataRelation) List[i]).RelationName;
555                                 if (String.Compare (name, name2, true) == 0) {
556                                         if (String.Compare (name, name2, false) == 0)
557                                                 return i;
558                                         match = i;
559                                         count++;
560                                 }
561                         }
562                         if (count == 1)
563                                 return match;
564                         if (count > 1 && error)
565                                 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
566                         return -1;
567                 }
568
569                 protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent)
570                 {
571                         if (CollectionChanged != null)
572                                 CollectionChanged (this, ccevent);
573                 }
574
575                 protected virtual void OnCollectionChanging (CollectionChangeEventArgs ccevent)
576                 {
577                         // LAME Spec: No associated events and it doesn't update CollectionChanged
578                         // event too as specified in MSDN
579                         // throw new NotImplementedException ();
580                 }
581
582                 public void Remove (DataRelation relation)
583                 {
584                         // To prevent endless recursion
585                         if (inTransition == relation)
586                                 return;
587
588                         inTransition = relation;
589
590                         if (relation == null)
591                                 return;
592
593                         try {
594                                 // check if the list doesnot contains this relation.
595                                 if (!(List.Contains (relation)))
596                                         throw new ArgumentException ("Relation doesnot belong to this Collection.");
597
598                                 OnCollectionChanging (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
599
600                                 RemoveCore (relation);
601                                 string name = "Relation" + index;
602                                 if (relation.RelationName == name)
603                                         index--;
604
605                                 OnCollectionChanged (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
606                         } finally {
607                                 inTransition = null;
608                         }
609                 }
610
611                 public void Remove (string name)
612                 {
613                         DataRelation relation = this [name];
614                         if (relation == null)
615                                 throw new ArgumentException ("Relation doesnot belong to this Collection.");
616                         Remove (relation);
617                 }
618
619                 public void RemoveAt (int index)
620                 {
621                         DataRelation relation = this [index];
622                         if (relation == null)
623                                 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}", index));
624                         Remove (relation);
625                 }
626
627                 protected virtual void RemoveCore (DataRelation relation)
628                 {
629                         // Remove from collection
630                         List.Remove (relation);
631                 }
632
633                 #region Events
634
635                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
636                 public event CollectionChangeEventHandler CollectionChanged;
637
638                 #endregion
639         }
640 }