2 // System.Data.DataColumnCollection.cs
5 // Christopher Podurgiel (cpodurgiel@msn.com)
6 // Stuart Caborn <stuart.caborn@virgin.net>
7 // Tim Coleman (tim@timcoleman.com)
10 // Copyright (C) Tim Coleman, 2002
11 // Copyright (C) Daniel Morgan, 2003
15 using System.Collections;
16 using System.ComponentModel;
18 namespace System.Data {
21 [DefaultEvent ("CollectionChanged")]
22 public class DataColumnCollection : InternalDataCollectionBase
24 //table should be the DataTable this DataColumnCollection belongs to.
25 private DataTable parentTable = null;
27 // Internal Constructor. This Class can only be created from other classes in this assembly.
28 internal DataColumnCollection(DataTable table):base()
34 /// Gets the DataColumn from the collection at the specified index.
36 public virtual DataColumn this[int index]
40 return (DataColumn) base.List[index];
45 /// Gets the DataColumn from the collection with the specified name.
47 public virtual DataColumn this[string name]
51 int tmp = IndexOf(name, true);
59 /// Gets a list of the DataColumnCollection items.
61 protected override ArrayList List
72 //DefaultValue set and AutoInc set check
73 //?Validate Expression??
74 //Name check and creation
76 //Check Unique if true then add a unique constraint
77 //?Notify Rows of new column ?
82 /// Creates and adds a DataColumn object to the DataColumnCollection.
84 /// <returns></returns>
85 public virtual DataColumn Add()
88 string defaultName = GetNextDefaultColumnName ();
89 DataColumn column = new DataColumn (defaultName);
90 CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
92 column.SetTable(parentTable);
93 base.List.Add(column);
95 column.SetOrdinal(Count - 1);
96 OnCollectionChanged(e);
100 private string GetNextDefaultColumnName ()
102 string defColumnName = "Column1";
103 for (int index = 2; Contains (defColumnName); ++index) {
104 defColumnName = "Column" + index;
106 return defColumnName;
110 /// Creates and adds the specified DataColumn object to the DataColumnCollection.
112 /// <param name="column">The DataColumn to add.</param>
114 public void Add(DataColumn column)
118 throw new ArgumentNullException ("column", "'column' argument cannot be null.");
120 if (column.ColumnName.Equals(String.Empty))
122 column.ColumnName = GetNextDefaultColumnName ();
124 int tmp = IndexOf(column.ColumnName);
125 // if we found a column with same name we have to check
126 // that it is the same case.
127 // indexof can return a table with different case letters.
130 if(column.ColumnName == this[tmp].ColumnName)
131 throw new DuplicateNameException("A DataColumn named '" + column.ColumnName + "' already belongs to this DataTable.");
134 if (column.Table != null)
135 throw new ArgumentException ("Column '" + column.ColumnName + "' already belongs to another DataTable.");
137 CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
139 column.SetTable (parentTable);
140 int ordinal = base.List.Add(column);
141 column.SetOrdinal (ordinal);
143 //add constraints if neccesary
147 UniqueConstraint uc = new UniqueConstraint(column);
148 parentTable.Constraints.Add(uc);
151 //TODO: add missing constraints. i.e. Primary/Foreign keys
153 OnCollectionChanged (e);
157 /// Creates and adds a DataColumn object with the specified name to the DataColumnCollection.
159 /// <param name="columnName">The name of the column.</param>
160 /// <returns>The newly created DataColumn.</returns>
161 public virtual DataColumn Add(string columnName)
163 if (columnName == null || columnName == String.Empty)
165 columnName = GetNextDefaultColumnName ();
168 DataColumn column = new DataColumn(columnName);
174 /// Creates and adds a DataColumn object with the specified name and type to the DataColumnCollection.
176 /// <param name="columnName">The ColumnName to use when cretaing the column.</param>
177 /// <param name="type">The DataType of the new column.</param>
178 /// <returns>The newly created DataColumn.</returns>
179 public virtual DataColumn Add(string columnName, Type type)
181 if (columnName == null || columnName == "")
183 columnName = GetNextDefaultColumnName ();
186 DataColumn column = new DataColumn(columnName, type);
192 /// Creates and adds a DataColumn object with the specified name, type, and expression to the DataColumnCollection.
194 /// <param name="columnName">The name to use when creating the column.</param>
195 /// <param name="type">The DataType of the new column.</param>
196 /// <param name="expression">The expression to assign to the Expression property.</param>
197 /// <returns>The newly created DataColumn.</returns>
198 public virtual DataColumn Add(string columnName, Type type, string expression)
200 if (columnName == null || columnName == "")
202 columnName = GetNextDefaultColumnName ();
205 DataColumn column = new DataColumn(columnName, type, expression);
211 /// Copies the elements of the specified DataColumn array to the end of the collection.
213 /// <param name="columns">The array of DataColumn objects to add to the collection.</param>
214 public void AddRange(DataColumn[] columns)
216 foreach (DataColumn column in columns)
224 /// Checks whether a given column can be removed from the collection.
226 /// <param name="column">A DataColumn in the collection.</param>
227 /// <returns>true if the column can be removed; otherwise, false.</returns>
228 public bool CanRemove(DataColumn column)
231 //Check that the column does not have a null reference.
238 //Check that the column is part of this collection.
239 if (!Contains(column.ColumnName))
246 //Check if this column is part of a relationship. (this could probably be written better)
247 foreach (DataRelation childRelation in parentTable.ChildRelations)
249 foreach (DataColumn childColumn in childRelation.ChildColumns)
251 if (childColumn == column)
257 foreach (DataColumn parentColumn in childRelation.ParentColumns)
259 if (parentColumn == column)
266 //Check if this column is part of a relationship. (this could probably be written better)
267 foreach (DataRelation parentRelation in parentTable.ParentRelations)
269 foreach (DataColumn childColumn in parentRelation.ChildColumns)
271 if (childColumn == column)
277 foreach (DataColumn parentColumn in parentRelation.ParentColumns)
279 if (parentColumn == column)
287 //Check if another column's expression depends on this column.
289 foreach (DataColumn dataColumn in List)
291 if (dataColumn.Expression.ToString().IndexOf(column.ColumnName) > 0)
297 //TODO: check constraints
303 /// Clears the collection of any columns.
307 CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this);
310 // FIXME: Hmm... This loop could look little nicer :)
311 foreach (DataColumn Col in List) {
313 foreach (DataRelation Rel in Col.Table.ParentRelations) {
315 foreach (DataColumn Col2 in Rel.ParentColumns) {
316 if (Object.ReferenceEquals (Col, Col2))
317 throw new ArgumentException ("Cannot remove this column, because " +
318 "it is part of the parent key for relationship " +
319 Rel.RelationName + ".");
322 foreach (DataColumn Col2 in Rel.ChildColumns) {
323 if (Object.ReferenceEquals (Col, Col2))
324 throw new ArgumentException ("Cannot remove this column, because " +
325 "it is part of the parent key for relationship " +
326 Rel.RelationName + ".");
331 foreach (DataRelation Rel in Col.Table.ChildRelations) {
333 foreach (DataColumn Col2 in Rel.ParentColumns) {
334 if (Object.ReferenceEquals (Col, Col2))
335 throw new ArgumentException ("Cannot remove this column, because " +
336 "it is part of the parent key for relationship " +
337 Rel.RelationName + ".");
340 foreach (DataColumn Col2 in Rel.ChildColumns) {
341 if (Object.ReferenceEquals (Col, Col2))
342 throw new ArgumentException ("Cannot remove this column, because " +
343 "it is part of the parent key for relationship " +
344 Rel.RelationName + ".");
351 OnCollectionChanged(e);
356 /// Checks whether the collection contains a column with the specified name.
358 /// <param name="name">The ColumnName of the column to check for.</param>
359 /// <returns>true if a column exists with this name; otherwise, false.</returns>
360 public bool Contains(string name)
362 return (IndexOf(name, false) != -1);
366 /// Gets the index of a column specified by name.
368 /// <param name="column">The name of the column to return.</param>
369 /// <returns>The index of the column specified by column if it is found; otherwise, -1.</returns>
370 public virtual int IndexOf(DataColumn column)
372 return base.List.IndexOf(column);
376 /// Gets the index of the column with the given name (the name is not case sensitive).
378 /// <param name="columnName">The name of the column to find.</param>
379 /// <returns>The zero-based index of the column with the specified name, or -1 if the column doesn't exist in the collection.</returns>
380 public int IndexOf(string columnName)
382 return IndexOf(columnName, false);
386 /// Raises the OnCollectionChanged event.
388 /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
389 protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent)
391 if (CollectionChanged != null)
393 CollectionChanged(this, ccevent);
398 /// Raises the OnCollectionChanging event.
400 /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
401 protected internal virtual void OnCollectionChanging(CollectionChangeEventArgs ccevent)
403 if (CollectionChanged != null)
405 //FIXME: this is not right
406 //CollectionChanged(this, ccevent);
407 throw new NotImplementedException();
412 /// Removes the specified DataColumn object from the collection.
414 /// <param name="column">The DataColumn to remove.</param>
415 public void Remove(DataColumn column)
417 if (IndexOf (column) == -1)
418 throw new ArgumentException ("Cannot remove a column that doesn't belong to this table.");
420 //TODO: can remove first with exceptions
421 //and OnChanging Event
422 CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Remove, this);
424 int ordinal = column.Ordinal;
425 base.List.Remove(column);
427 //Update the ordinals
428 for( int i = ordinal ; i < this.Count ; i ++ )
430 this[i].SetOrdinal( i );
433 OnCollectionChanged(e);
438 /// Removes the DataColumn object with the specified name from the collection.
440 /// <param name="name">The name of the column to remove.</param>
441 public void Remove(string name)
443 DataColumn column = this[name];
446 throw new ArgumentException ("Column '" + name + "' does not belong to table test_table.");
452 /// Removes the column at the specified index from the collection.
454 /// <param name="index">The index of the column to remove.</param>
455 public void RemoveAt(int index)
458 throw new IndexOutOfRangeException ("Cannot find column " + index + ".");
460 DataColumn column = this[index];
466 /// Do the same as Constains -method but case sensitive
468 private bool CaseSensitiveContains(string columnName)
471 DataColumn column = this[columnName];
474 return string.Compare (column.ColumnName, columnName, false) == 0;
479 private int IndexOf (string name, bool error)
481 int count = 0, match = -1;
482 for (int i = 0; i < List.Count; i++)
484 String name2 = ((DataColumn) List[i]).ColumnName;
485 if (String.Compare (name, name2, true) == 0)
487 if (String.Compare (name, name2, false) == 0)
495 if (count > 1 && error)
496 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
502 /// Occurs when the columns collection changes, either by adding or removing a column.
504 public event CollectionChangeEventHandler CollectionChanged;