2003-11-09 Pedro Mart�nez Juli� <yoros@wanadoo.es>
[mono.git] / mcs / class / System.Data / System.Data / DataColumnCollection.cs
1 //
2 // System.Data.DataColumnCollection.cs
3 //
4 // Author:
5 //   Christopher Podurgiel (cpodurgiel@msn.com)
6 //   Stuart Caborn      <stuart.caborn@virgin.net>
7 //   Tim Coleman (tim@timcoleman.com)
8 //
9 // (C) Chris Podurgiel
10 // Copyright (C) Tim Coleman, 2002
11 // Copyright (C) Daniel Morgan, 2003
12 //
13
14 using System;
15 using System.Collections;
16 using System.ComponentModel;
17
18 namespace System.Data {
19         [Editor]
20         [Serializable]
21         [DefaultEvent ("CollectionChanged")]
22         public class DataColumnCollection : InternalDataCollectionBase
23         {
24                 //table should be the DataTable this DataColumnCollection belongs to.
25                 private DataTable parentTable = null;
26
27                 // Internal Constructor.  This Class can only be created from other classes in this assembly.
28                 internal DataColumnCollection(DataTable table):base()
29                 {
30                         parentTable = table;
31                 }
32
33                 /// <summary>
34                 /// Gets the DataColumn from the collection at the specified index.
35                 /// </summary>
36                 public virtual DataColumn this[int index]
37                 {
38                         get
39                         {
40                                 return (DataColumn) base.List[index];
41                         }
42                 }
43
44                 /// <summary>
45                 /// Gets the DataColumn from the collection with the specified name.
46                 /// </summary>
47                 public virtual DataColumn this[string name]
48                 {
49                         get
50                         {
51                                 foreach (DataColumn column in base.List)
52                                 {                                       
53                                         if (String.Compare (column.ColumnName, name, true) == 0)
54                                         {
55                                                 return column;
56                                         }
57                                 }
58                                 return null;                
59                         }
60                 }
61
62                 /// <summary>
63                 /// Gets a list of the DataColumnCollection items.
64                 /// </summary>
65                 protected override ArrayList List 
66                 {
67                         get
68                         {
69                                 return base.List;
70                         }
71                 }
72
73                 //Add Logic
74                 //
75                 //Changing Event
76                 //DefaultValue set and AutoInc set check
77                 //?Validate Expression??
78                 //Name check and creation
79                 //Set Table
80                 //Check Unique if true then add a unique constraint
81                 //?Notify Rows of new column ?
82                 //Add to collection
83                 //Changed Event
84
85                 /// <summary>
86                 /// Creates and adds a DataColumn object to the DataColumnCollection.
87                 /// </summary>
88                 /// <returns></returns>
89                 public virtual DataColumn Add()
90                 {
91                         //FIXME:
92                         string defaultName = GetNextDefaultColumnName ();
93                         DataColumn column = new DataColumn (defaultName);
94                         CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
95                         
96                         column.SetTable(parentTable);
97                         base.List.Add(column);
98                         
99                         column.SetOrdinal(Count - 1);
100                         OnCollectionChanged(e);
101                         return column;
102                 }
103
104                 private string GetNextDefaultColumnName ()
105                 {
106                         string defColumnName = "Column1";
107                         for (int index = 2; Contains (defColumnName); ++index) {
108                                 defColumnName = "Column" + index;
109                         }
110                         return defColumnName;
111                 }
112
113                 /// <summary>
114                 /// Creates and adds the specified DataColumn object to the DataColumnCollection.
115                 /// </summary>
116                 /// <param name="column">The DataColumn to add.</param>
117                 [MonoTODO]
118                 public void Add(DataColumn column)
119                 {
120
121                         if (column == null)
122                                 throw new ArgumentNullException ("column", "'column' argument cannot be null.");
123
124                         if (column.ColumnName.Equals(String.Empty))
125                         {
126                                 column.ColumnName = GetNextDefaultColumnName ();
127                         }
128                         else if (Contains(column.ColumnName))
129                         {
130                                 if (object.ReferenceEquals(this [column.ColumnName], column))
131                                         throw new ArgumentException ("Column '" + column.ColumnName + "' already belongs to this DataTable.");
132                                 else if (CaseSensitiveContains (column.ColumnName))
133                                         throw new DuplicateNameException("A column named '" + column.ColumnName + "' already belongs to this DataTable.");
134                         }
135
136                         if (column.Table != null)
137                                 throw new ArgumentException ("Column '" + column.ColumnName + "' already belongs to another DataTable.");
138
139                         CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
140
141                         column.SetTable (parentTable);
142                         int ordinal = base.List.Add(column);
143                         column.SetOrdinal (ordinal);
144
145                         //add constraints if neccesary
146
147                         if (column.Unique)
148                         {
149                                 UniqueConstraint uc = new UniqueConstraint(column);
150                                 parentTable.Constraints.Add(uc);
151                         }
152
153                         //TODO: add missing constraints. i.e. Primary/Foreign keys
154
155                         OnCollectionChanged (e);
156                 }
157
158                 /// <summary>
159                 /// Creates and adds a DataColumn object with the specified name to the DataColumnCollection.
160                 /// </summary>
161                 /// <param name="columnName">The name of the column.</param>
162                 /// <returns>The newly created DataColumn.</returns>
163                 public virtual DataColumn Add(string columnName)
164                 {
165                         if (columnName == null || columnName == String.Empty)
166                         {
167                                 columnName = GetNextDefaultColumnName ();
168                         }
169                         
170                         DataColumn column = new DataColumn(columnName);
171                         Add (column);
172                         return column;
173                 }
174
175                 /// <summary>
176                 /// Creates and adds a DataColumn object with the specified name and type to the DataColumnCollection.
177                 /// </summary>
178                 /// <param name="columnName">The ColumnName to use when cretaing the column.</param>
179                 /// <param name="type">The DataType of the new column.</param>
180                 /// <returns>The newly created DataColumn.</returns>
181                 public virtual DataColumn Add(string columnName, Type type)
182                 {
183                         if (columnName == null || columnName == "")
184                         {
185                                 columnName = GetNextDefaultColumnName ();
186                         }
187                         
188                         DataColumn column = new DataColumn(columnName, type);
189                         Add (column);
190                         return column;
191                 }
192
193                 /// <summary>
194                 /// Creates and adds a DataColumn object with the specified name, type, and expression to the DataColumnCollection.
195                 /// </summary>
196                 /// <param name="columnName">The name to use when creating the column.</param>
197                 /// <param name="type">The DataType of the new column.</param>
198                 /// <param name="expression">The expression to assign to the Expression property.</param>
199                 /// <returns>The newly created DataColumn.</returns>
200                 public virtual DataColumn Add(string columnName, Type type, string expression)
201                 {
202                         if (columnName == null || columnName == "")
203                         {
204                                 columnName = GetNextDefaultColumnName ();
205                         }
206                         
207                         DataColumn column = new DataColumn(columnName, type, expression);
208                         Add (column);
209                         return column;
210                 }
211
212                 /// <summary>
213                 /// Copies the elements of the specified DataColumn array to the end of the collection.
214                 /// </summary>
215                 /// <param name="columns">The array of DataColumn objects to add to the collection.</param>
216                 public void AddRange(DataColumn[] columns)
217                 {
218                         foreach (DataColumn column in columns)
219                         {
220                                 Add(column);
221                         }
222                         return;
223                 }
224
225                 /// <summary>
226                 /// Checks whether a given column can be removed from the collection.
227                 /// </summary>
228                 /// <param name="column">A DataColumn in the collection.</param>
229                 /// <returns>true if the column can be removed; otherwise, false.</returns>
230                 public bool CanRemove(DataColumn column)
231                 {
232                                                 
233                         //Check that the column does not have a null reference.
234                         if (column == null)
235                         {
236                 return false;
237                         }
238
239                         
240                         //Check that the column is part of this collection.
241                         if (!Contains(column.ColumnName))
242                         {
243                                 return false;
244                         }
245
246
247                         
248                         //Check if this column is part of a relationship. (this could probably be written better)
249                         foreach (DataRelation childRelation in parentTable.ChildRelations)
250                         {
251                                 foreach (DataColumn childColumn in childRelation.ChildColumns)
252                                 {
253                                         if (childColumn == column)
254                                         {
255                                                 return false;
256                                         }
257                                 }
258
259                                 foreach (DataColumn parentColumn in childRelation.ParentColumns)
260                                 {
261                                         if (parentColumn == column)
262                                         {
263                                                 return false;
264                                         }
265                                 }
266                         }
267
268                         //Check if this column is part of a relationship. (this could probably be written better)
269                         foreach (DataRelation parentRelation in parentTable.ParentRelations)
270                         {
271                                 foreach (DataColumn childColumn in parentRelation.ChildColumns)
272                                 {
273                                         if (childColumn == column)
274                                         {
275                                                 return false;
276                                         }
277                                 }
278
279                                 foreach (DataColumn parentColumn in parentRelation.ParentColumns)
280                                 {
281                                         if (parentColumn == column)
282                                         {
283                                                 return false;
284                                         }
285                                 }
286                         }
287
288                         
289                         //Check if another column's expression depends on this column.
290                         
291                         foreach (DataColumn dataColumn in List)
292                         {
293                                 if (dataColumn.Expression.ToString().IndexOf(column.ColumnName) > 0)
294                                 {
295                                         return false;
296                                 }
297                         }
298                         
299                         //TODO: check constraints
300
301                         return true;
302                 }
303
304                 /// <summary>
305                 /// Clears the collection of any columns.
306                 /// </summary>
307                 public void Clear()
308                 {
309                         CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this);
310
311
312                         // FIXME: Hmm... This loop could look little nicer :)
313                         foreach (DataColumn Col in List) {
314
315                                 foreach (DataRelation Rel in Col.Table.ParentRelations) {
316
317                                         foreach (DataColumn Col2 in Rel.ParentColumns) {
318                                                 if (Object.ReferenceEquals (Col, Col2))
319                                                         throw new ArgumentException ("Cannot remove this column, because " + 
320                                                                                      "it is part of the parent key for relationship " + 
321                                                                                      Rel.RelationName + ".");
322                                         }
323
324                                         foreach (DataColumn Col2 in Rel.ChildColumns) {
325                                                 if (Object.ReferenceEquals (Col, Col2))
326                                                         throw new ArgumentException ("Cannot remove this column, because " + 
327                                                                                      "it is part of the parent key for relationship " + 
328                                                                                      Rel.RelationName + ".");
329
330                                         }
331                                 }
332
333                                 foreach (DataRelation Rel in Col.Table.ChildRelations) {
334
335                                         foreach (DataColumn Col2 in Rel.ParentColumns) {
336                                                 if (Object.ReferenceEquals (Col, Col2))
337                                                         throw new ArgumentException ("Cannot remove this column, because " + 
338                                                                                      "it is part of the parent key for relationship " + 
339                                                                                      Rel.RelationName + ".");
340                                         }
341
342                                         foreach (DataColumn Col2 in Rel.ChildColumns) {
343                                                 if (Object.ReferenceEquals (Col, Col2))
344                                                         throw new ArgumentException ("Cannot remove this column, because " + 
345                                                                                      "it is part of the parent key for relationship " + 
346                                                                                      Rel.RelationName + ".");
347                                         }
348                                 }
349
350                         }
351                                         
352                         base.List.Clear();
353                         OnCollectionChanged(e);
354                         return;
355                 }
356
357                 /// <summary>
358                 /// Checks whether the collection contains a column with the specified name.
359                 /// </summary>
360                 /// <param name="name">The ColumnName of the column to check for.</param>
361                 /// <returns>true if a column exists with this name; otherwise, false.</returns>
362                 public bool Contains(string name)
363                 {
364                         return (IndexOf(name) != -1);
365                 }
366
367                 /// <summary>
368                 /// Gets the index of a column specified by name.
369                 /// </summary>
370                 /// <param name="column">The name of the column to return.</param>
371                 /// <returns>The index of the column specified by column if it is found; otherwise, -1.</returns>
372                 public virtual int IndexOf(DataColumn column)
373                 {
374                         return base.List.IndexOf(column);
375                 }
376
377                 /// <summary>
378                 /// Gets the index of the column with the given name (the name is not case sensitive).
379                 /// </summary>
380                 /// <param name="columnName">The name of the column to find.</param>
381                 /// <returns>The zero-based index of the column with the specified name, or -1 if the column doesn't exist in the collection.</returns>
382                 public int IndexOf(string columnName)
383                 {
384                         
385                         DataColumn column = this[columnName];
386                         
387                         if (column != null)
388                         {
389                                 return IndexOf(column);
390                         }
391                         else
392                         {
393                                 return -1;
394                         }
395                 }
396
397                 /// <summary>
398                 /// Raises the OnCollectionChanged event.
399                 /// </summary>
400                 /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
401                 protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent)
402                 {
403                         if (CollectionChanged != null) 
404                         {
405                                 CollectionChanged(this, ccevent);
406                         }
407                 }
408
409                 /// <summary>
410                 /// Raises the OnCollectionChanging event.
411                 /// </summary>
412                 /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
413                 protected internal virtual void OnCollectionChanging(CollectionChangeEventArgs ccevent)
414                 {
415                         if (CollectionChanged != null) 
416                         {
417                                 //FIXME: this is not right
418                                 //CollectionChanged(this, ccevent);
419                                 throw new NotImplementedException();
420                         }
421                 }
422
423                 /// <summary>
424                 /// Removes the specified DataColumn object from the collection.
425                 /// </summary>
426                 /// <param name="column">The DataColumn to remove.</param>
427                 public void Remove(DataColumn column)
428                 {
429                         if (IndexOf (column) == -1)
430                                 throw new ArgumentException ("Cannot remove a column that doesn't belong to this table.");
431
432                         //TODO: can remove first with exceptions
433                         //and OnChanging Event
434                         CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Remove, this);
435                         
436                         int ordinal = column.Ordinal;
437                         base.List.Remove(column);
438                         
439                         //Update the ordinals
440                         for( int i = ordinal ; i < this.Count ; i ++ )
441                         {
442                                 this[i].SetOrdinal( i );
443                         }
444                         
445                         OnCollectionChanged(e);
446                         return;
447                 }
448
449                 /// <summary>
450                 /// Removes the DataColumn object with the specified name from the collection.
451                 /// </summary>
452                 /// <param name="name">The name of the column to remove.</param>
453                 public void Remove(string name)
454                 {
455                         DataColumn column = this[name];
456                         
457                         if (column == null)
458                                 throw new ArgumentException ("Column '" + name + "' does not belong to table test_table.");
459
460                         Remove( column );
461                 }
462
463                 /// <summary>
464                 /// Removes the column at the specified index from the collection.
465                 /// </summary>
466                 /// <param name="index">The index of the column to remove.</param>
467                 public void RemoveAt(int index)
468                 {
469                         if (Count <= index)
470                                 throw new IndexOutOfRangeException ("Cannot find column " + index + ".");
471
472                         DataColumn column = this[index];
473                         Remove( column );
474                 }
475
476
477                 /// <summary>
478                 ///  Do the same as Constains -method but case sensitive
479                 /// </summary>
480                 private bool CaseSensitiveContains(string columnName)
481                 {
482                         
483                         DataColumn column = this[columnName];
484                         
485                         if (column != null)
486                                 return string.Compare (column.ColumnName, columnName, false) == 0; 
487
488                         return false;
489                 }
490                 
491
492                 /// <summary>
493                 /// Occurs when the columns collection changes, either by adding or removing a column.
494                 /// </summary>
495                 public event CollectionChangeEventHandler CollectionChanged;
496         }
497 }