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