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