2003-11-25 Tim Coleman <tim@timcoleman.com>
[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 using System;
16 using System.Collections;
17 using System.ComponentModel;
18
19 namespace System.Data {
20         /// <summary>
21         /// Represents the collection of DataRelation objects for this DataSet.
22         /// </summary>
23         [Editor]
24         [DefaultEvent ("CollectionChanged")]
25         [Serializable]
26         public abstract class DataRelationCollection : InternalDataCollectionBase
27         {
28                 /// <summary>
29                 /// Summary description for DataTableRelationCollection.
30                 /// </summary>
31                 internal class DataSetRelationCollection : DataRelationCollection
32                 {
33                         private DataSet dataSet;
34                         
35                         /// <summary>
36                         /// Initializes a new instance of the DataSetRelationCollection class.
37                         /// </summary>
38                         internal DataSetRelationCollection (DataSet dataSet)
39                         {
40                                 this.dataSet = dataSet;
41                         }
42
43                         /// <summary>
44                         /// Gets the DataRelation object specified by name.
45                         /// </summary>
46                         public override DataRelation this [string name]
47                         {
48                                 get {
49                                         foreach (DataRelation dataRelation in List)
50                                                 if (dataRelation.RelationName == name) return dataRelation;
51                                         return null;
52                                 }
53                         }
54
55                         /// <summary>
56                         /// Gets the DataRelation object at the specified index.
57                         /// </summary>
58                         public override DataRelation this [int index]
59                         {
60                                 get {
61                                         return List [index] as DataRelation;
62                                 }
63                         }
64
65                         protected override DataSet GetDataSet()
66                         {
67                                 return dataSet;
68                         }
69
70                         /// <summary>
71                         /// Performs verification on the table.
72                         /// </summary>
73                         /// <param name="relation">The relation to check.</param>
74                         protected override void AddCore (DataRelation relation)
75                         {
76                                 base.AddCore (relation);
77                                 if (relation.ChildTable.DataSet != this.dataSet || relation.ParentTable.DataSet != this.dataSet)
78                                         throw new DataException ();
79                                 //List.Add (relation);
80                                 relation.SetDataSet (dataSet);
81                                 relation.ParentTable.ChildRelations.Add (relation);
82                                 relation.ChildTable.ParentRelations.Add (relation);
83                                 ForeignKeyConstraint foreignKeyConstraint = null;
84                                 if (relation.createConstraints) 
85                                 {
86                                         foreignKeyConstraint = new ForeignKeyConstraint (relation.RelationName, relation.ParentColumns, relation.ChildColumns);
87                                         relation.ChildTable.Constraints.Add (foreignKeyConstraint);
88                                 
89                                         UniqueConstraint uniqueConstraint = null;
90                                         ConstraintCollection parentConstrains = relation.ParentTable.Constraints;
91                                         // find if the unique constraint already exists in the parent table.
92                                         foreach (Constraint o in parentConstrains) 
93                                         {
94                                                 if (o is UniqueConstraint) 
95                                                 {
96                                                         UniqueConstraint uc = (UniqueConstraint) o;
97                                                         if (uc.Columns.Length == relation.ParentColumns.Length) 
98                                                         {
99                                                                 bool allColumnsEqual = true;
100                                                                 for (int columnCnt = 0; columnCnt < uc.Columns.Length; ++columnCnt) 
101                                                                 {
102                                                                         if (uc.Columns[columnCnt] != relation.ParentColumns[columnCnt]) 
103                                                                         {
104                                                                                 allColumnsEqual = false;
105                                                                                 break;
106                                                                         }
107                                                                 }
108                                                                 if (allColumnsEqual) 
109                                                                 {
110                                                                         uniqueConstraint = uc;
111                                                                         break;
112                                                                 }
113                                                         }
114                                                 }
115                                         }
116                                         // if we did not find the unique constraint in the parent table.
117                                         // we generate new uniqueconastraint and add it to the parent table.
118                                         if (uniqueConstraint == null)
119                                         {
120                                                 uniqueConstraint = new UniqueConstraint(relation.ParentColumns, false);
121                                                 relation.ParentTable.Constraints.Add(uniqueConstraint);
122                                         }
123                                         relation.SetParentKeyConstraint (uniqueConstraint);
124                                         relation.SetChildKeyConstraint (foreignKeyConstraint);
125                                 }
126                         }
127
128                         public override void AddRange (DataRelation[] relations)
129                         {
130                                 base.AddRange (relations);
131                         }
132
133                         public override void Clear ()
134                         {
135                                 for (int i = 0; i < Count; i++)
136                                         RemoveCore(this[i]);
137
138                                 base.Clear();
139                         }
140
141                         protected override void RemoveCore (DataRelation relation)
142                         {
143                                 relation.SetDataSet (null);
144                                 relation.ParentTable.ChildRelations.Remove (relation);
145                                 relation.ChildTable.ParentRelations.Remove (relation);
146                                 relation.SetParentKeyConstraint (null);
147                                 relation.SetChildKeyConstraint (null);
148                         }
149
150                         protected override ArrayList List {
151                                 get {
152                                         return base.List;
153                                 }
154                         }
155                 }
156
157                 /// <summary>
158                 /// Summary description for DataTableRelationCollection.
159                 /// </summary>
160                 internal class DataTableRelationCollection : DataRelationCollection
161                 {
162                         private DataTable dataTable;
163                         
164                         /// <summary>
165                         /// Initializes a new instance of the DataTableRelationCollection class.
166                         /// </summary>
167                         internal DataTableRelationCollection (DataTable dataTable)
168                         {
169                                 this.dataTable = dataTable;
170                         }
171
172                         /// <summary>
173                         /// Gets the DataRelation object specified by name.
174                         /// </summary>
175                         public override DataRelation this [string name]
176                         {
177                                 get {
178                                         foreach (DataRelation dataRelation in List)
179                                                 if (dataRelation.RelationName == name) return dataRelation;
180                                         return null;
181                                 }
182                         }
183
184                         /// <summary>
185                         /// Gets the DataRelation object at the specified index.
186                         /// </summary>
187                         public override DataRelation this [int index]
188                         {
189                                 get {
190                                         return List [index] as DataRelation;
191                                 }
192                         }
193
194                         protected override DataSet GetDataSet()
195                         {
196                                 return dataTable.DataSet;
197                         }
198
199                         protected override void AddCore (DataRelation relation)
200                         {
201                                 base.AddCore (relation);
202                         }
203
204                         protected override void RemoveCore (DataRelation relation)
205                         {
206                                 base.RemoveCore (relation);
207                         }
208
209                         protected override ArrayList List {
210                                 get {
211                                         return base.List;
212                                 }
213                         }
214                 }
215
216                 private int defaultNameIndex;
217                 private bool inTransition;
218                 int index;
219
220                 
221                 /// <summary>
222                 /// Initializes a new instance of the DataRelationCollection class.
223                 /// </summary>
224                 protected DataRelationCollection () 
225                         : base ()
226                 {
227                         defaultNameIndex = 1;
228                         inTransition = false;
229                 }
230
231                 /// <summary>
232                 /// Gets the DataRelation object specified by name.
233                 /// </summary>
234                 public abstract DataRelation this[string name]{get;}
235
236                 /// <summary>
237                 /// Gets the DataRelation object at the specified index.
238                 /// </summary>
239                 public abstract DataRelation this[int index]{get;}
240
241                 
242                 #region Add Methods
243                 private string GetNextDefaultRelationName ()
244                 {
245                         string defRelationName = "Relation";
246                         for (int index = 1; Contains (defRelationName); ++index) {
247                                 defRelationName = "Relation" + index;
248                         }
249                         return defRelationName;
250                 }
251
252                 /// <summary>
253                 /// Adds a DataRelation to the DataRelationCollection.
254                 /// </summary>
255                 /// <param name="relation">The DataRelation to add to the collection.</param>
256                 [MonoTODO]
257                 public void Add(DataRelation relation)
258                 {
259                         this.AddCore (relation);
260                         if(relation.RelationName == string.Empty)
261                                 relation.RelationName = GenerateRelationName();
262                         CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
263                         List.Add(relation);
264                         OnCollectionChanged(e);
265                 }
266
267                 private string GenerateRelationName()
268                 {
269                         index++;
270                         return "Relation" + index;
271                 }
272
273                 /// <summary>
274                 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
275                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
276                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
277                 /// The CollectionChanged event is fired if it succeeds.
278                 /// </summary>
279                 /// <param name="parentColumn">parent column of relation.</param>
280                 /// <param name="childColumn">child column of relation.</param>
281                 /// <returns>The created DataRelation.</returns>
282                 public virtual DataRelation Add(DataColumn parentColumn, DataColumn childColumn)
283                 {       
284                         DataRelation dataRelation = new DataRelation(GetNextDefaultRelationName (), parentColumn, childColumn);
285                         Add(dataRelation);
286                         return dataRelation;
287                 }
288
289                 /// <summary>
290                 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
291                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
292                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
293                 /// The CollectionChanged event is raised if it succeeds.
294                 /// </summary>
295                 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
296                 /// <param name="childColumns">An array of child DataColumn objects.</param>
297                 /// <returns>The created DataRelation.</returns>
298                 public virtual DataRelation Add(DataColumn[] parentColumns, DataColumn[] childColumns)
299                 {
300                         DataRelation dataRelation = new DataRelation(GetNextDefaultRelationName (), parentColumns, childColumns);
301                         Add(dataRelation);
302                         return dataRelation;
303                 }
304
305                 /// <summary>
306                 /// Creates a relation given the parameters and adds it to the collection.
307                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
308                 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
309                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
310                 /// The CollectionChanged event is raised if it succeeds.
311                 /// </summary>
312                 /// <param name="name">The name of the relation.</param>
313                 /// <param name="parentColumn">parent column of relation.</param>
314                 /// <returns>The created DataRelation.</returns>
315                 /// <returns></returns>
316                 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn)
317                 {
318                         //If no name was supplied, give it a default name.
319                         if (name == null || name == "") name = GetNextDefaultRelationName ();
320
321                         DataRelation dataRelation = new DataRelation(name, parentColumn, childColumn);
322                         Add(dataRelation);
323                         return dataRelation;
324                 }
325
326                 /// <summary>
327                 /// Creates a DataRelation with the specified name, and arrays of parent and child columns, and adds it to the collection.
328                 /// </summary>
329                 /// <param name="name">The name of the DataRelation to create.</param>
330                 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
331                 /// <param name="childColumns">An array of child DataColumn objects.</param>
332                 /// <returns>The created DataRelation.</returns>
333                 public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns)
334                 {
335                         //If no name was supplied, give it a default name.
336                         if (name == null || name == "") name = GetNextDefaultRelationName ();
337
338                         DataRelation dataRelation = new DataRelation(name, parentColumns, childColumns);
339                         Add(dataRelation);
340                         return dataRelation;
341                 }
342
343                 /// <summary>
344                 /// Creates a relation given the parameters and adds it to the collection.
345                 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
346                 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
347                 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
348                 /// The CollectionChanged event is raised if it succeeds.
349                 /// </summary>
350                 /// <param name="name">The name of the relation.</param>
351                 /// <param name="parentColumn">parent column of relation.</param>
352                 /// <param name="childColumn">child column of relation.</param>
353                 /// <param name="createConstraints">true to create constraints; otherwise false. (default is true)</param>
354                 /// <returns>The created DataRelation.</returns>
355                 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
356                 {
357                         //If no name was supplied, give it a default name.
358                         if (name == null || name == "") name = GetNextDefaultRelationName ();
359
360                         DataRelation dataRelation = new DataRelation(name, parentColumn, childColumn, createConstraints);
361                         Add(dataRelation);
362                         return dataRelation;
363                 }
364
365                 /// <summary>
366                 /// Creates a DataRelation with the specified name, arrays of parent and child columns, 
367                 /// and value specifying whether to create a constraint, and adds it to the collection.
368                 /// </summary>
369                 /// <param name="name">The name of the DataRelation to create.</param>
370                 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
371                 /// <param name="childColumns">An array of child DataColumn objects.</param>
372                 /// <param name="createConstraints">true to create a constraint; otherwise false.</param>
373                 /// <returns>The created DataRelation.</returns>
374                 public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints)
375                 {
376                         //If no name was supplied, give it a default name.
377                         if (name == null || name == "") name = GetNextDefaultRelationName ();
378
379                         DataRelation dataRelation = new DataRelation(name, parentColumns, childColumns, createConstraints);
380                         Add(dataRelation);
381                         return dataRelation;
382                 }
383                 #endregion
384         
385                 /// <summary>
386                 /// Performs verification on the table.
387                 /// </summary>
388                 /// <param name="relation">The relation to check.</param>
389                 [MonoTODO]
390                 protected virtual void AddCore(DataRelation relation)
391                 {
392                         if (relation == null)
393                         {
394                                 //TODO: Issue a good exception message.
395                                 throw new ArgumentNullException();
396                         }
397                         else if(List.IndexOf(relation) != -1)
398                         {
399                                 //TODO: Issue a good exception message.
400                                 throw new ArgumentException();
401                         }
402                         else if(List.Contains(relation.RelationName))
403                         {
404                                 //TODO: Issue a good exception message.
405                                 throw new DuplicateNameException("A Relation named " + relation.RelationName + " already belongs to this DataSet.");
406                         }
407                 }
408
409                 /// <summary>
410                 /// Copies the elements of the specified DataRelation array to the end of the collection.
411                 /// </summary>
412                 /// <param name="relations">The array of DataRelation objects to add to the collection.</param>
413                 public virtual void AddRange(DataRelation[] relations)
414                 {
415                         foreach (DataRelation relation in relations) Add(relation);
416                 }
417
418                 [MonoTODO]
419                 public virtual bool CanRemove(DataRelation relation)
420                 {
421                         //TODO: Implement.
422                         return false;
423                 }
424
425                 public virtual void Clear()
426                 {
427                         List.Clear();
428                 }
429
430                 public virtual bool Contains(string name)
431                 {
432                         return IndexOf(name) != -1;
433                 }
434
435                 private CollectionChangeEventArgs CreateCollectionChangeEvent (CollectionChangeAction action)
436                 {
437                         return new CollectionChangeEventArgs (action, this);
438                 }
439
440                 protected abstract DataSet GetDataSet();
441
442                 public virtual int IndexOf(DataRelation relation)
443                 {
444                         return List.IndexOf(relation);
445                 }
446
447                 public virtual int IndexOf(string relationName)
448                 {
449                         return List.IndexOf(this[relationName]);
450                 }
451
452                 protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent)
453                 {
454                         if (CollectionChanged != null)
455                                 CollectionChanged (this, ccevent);
456                 }
457
458                 [MonoTODO]
459                 protected internal virtual void OnCollectionChanging (CollectionChangeEventArgs ccevent)
460                 {
461                         throw new NotImplementedException ();
462                 }
463
464                 public void Remove (DataRelation relation)
465                 {
466                         RemoveCore (relation);
467                         List.Remove (relation);
468                         string name = "Relation" + index;
469                         if (relation.RelationName == name)
470                                 index--;
471                         OnCollectionChanged (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
472                 }
473
474                 public void Remove (string name)
475                 {
476                         Remove ((DataRelation) List[IndexOf (name)]);
477                 }
478
479                 public void RemoveAt (int index)
480                 {
481                         Remove(this[index]);
482                 }
483
484                 [MonoTODO]
485                 protected virtual void RemoveCore(DataRelation relation)
486                 {
487                         // TODO: What have to be done?
488                 }
489
490                 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
491                 public event CollectionChangeEventHandler CollectionChanged;
492         }
493 }