copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / System.Data / System.Data / DataColumnCollection.cs
index 49539cda6fa1a38cd37a136ac577565f86be4ec6..aea50b166910c7ae7cafdd45a7fd1705bbaa83d7 100644 (file)
 // distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so, subject to
 // the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be
 // included in all copies or substantial portions of the Software.
-// 
+//
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 //
 
 using System;
+using System.Text;
 using System.Collections;
 using System.ComponentModel;
 
 namespace System.Data {
-       [Editor]
+
+       internal class Doublet
+       {
+               public Doublet (int count, string columnname)
+               {
+                       this.count = count;
+                       this.columnNames.Add (columnname);
+               }
+               // Number of case insensitive column name
+               public int count;
+               // Array of exact column names
+               public ArrayList columnNames = new ArrayList ();
+       }
+
+       [Editor ("Microsoft.VSDesigner.Data.Design.ColumnsCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
+                "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
+#if !NET_2_0
        [Serializable]
+#endif
        [DefaultEvent ("CollectionChanged")]
-       public class DataColumnCollection : InternalDataCollectionBase
-       {
+       public
+#if NET_2_0
+       sealed
+#endif
+       class DataColumnCollection : InternalDataCollectionBase {
+               //This hashtable maps between unique case insensetive column name to a doublet containing column ref and column count
+#if NET_2_0
+               private Hashtable columnNameCount = new Hashtable (StringComparer.OrdinalIgnoreCase);
+#else
+               private Hashtable columnNameCount = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
+#endif
                //This hashtable maps between column name to DataColumn object.
-               private Hashtable columnFromName = new Hashtable();
+               private Hashtable columnFromName = new Hashtable ();
                //This ArrayList contains the auto-increment columns names
-               private ArrayList autoIncrement = new ArrayList();
+               private ArrayList autoIncrement = new ArrayList ();
                //This holds the next index to use for default column name.
                private int defaultColumnIndex = 1;
                //table should be the DataTable this DataColumnCollection belongs to.
                private DataTable parentTable = null;
+               // Keep reference to most recent columns passed to AddRange()
+               // so that they can be added when EndInit() is called.
+               DataColumn [] _mostRecentColumns = null;
+
+               static readonly string ColumnPrefix = "Column";
 
                // Internal Constructor.  This Class can only be created from other classes in this assembly.
-               internal DataColumnCollection(DataTable table):base()
+               internal DataColumnCollection (DataTable table)
                {
                        parentTable = table;
                }
@@ -62,50 +94,50 @@ namespace System.Data {
                /// <summary>
                /// Gets the DataColumn from the collection at the specified index.
                /// </summary>
-               public virtual DataColumn this[int index]
-               {
-                       get
-                       {
-                               return (DataColumn) base.List[index];
+               public
+#if !NET_2_0
+               virtual
+#endif
+               DataColumn this [int index] {
+                       get {
+                               if (index < 0 || index >= base.List.Count)
+                                       throw new IndexOutOfRangeException ("Cannot find column " + index + ".");
+                               return (DataColumn) base.List [index];
                        }
                }
 
                /// <summary>
                /// Gets the DataColumn from the collection with the specified name.
                /// </summary>
-               public virtual DataColumn this[string name]
-               {
-                       get
-                       {
-                               DataColumn dc = columnFromName[name] as DataColumn;
-                               
+               public
+#if !NET_2_0
+               virtual
+#endif
+               DataColumn this [string name] {
+                       get {
+#if NET_2_0
+                               if (name == null)
+                                       throw new ArgumentNullException ("name");
+#endif
+
+                               DataColumn dc = columnFromName [name] as DataColumn;
                                if (dc != null)
                                        return dc;
 
-                               int tmp = IndexOf(name, true);
-                               if (tmp == -1)
-                                       return null;
-                               return this[tmp]; 
+                               int tmp = IndexOf (name, true);
+                               return tmp == -1 ? null : (DataColumn) base.List [tmp];
                        }
                }
 
                /// <summary>
                /// Gets a list of the DataColumnCollection items.
                /// </summary>
-               protected override ArrayList List 
-               {
-                       get
-                       {
-                               return base.List;
-                       }
+               protected override ArrayList List {
+                       get { return base.List; }
                }
 
-               internal ArrayList AutoIncrmentColumns 
-               {
-                       get
-                       {
-                               return autoIncrement;
-                       }
+               internal ArrayList AutoIncrmentColumns {
+                       get { return autoIncrement; }
                }
 
                //Add Logic
@@ -124,51 +156,89 @@ namespace System.Data {
                /// Creates and adds a DataColumn object to the DataColumnCollection.
                /// </summary>
                /// <returns></returns>
-               public virtual DataColumn Add()
+               public
+#if !NET_2_0
+               virtual
+#endif
+               DataColumn Add ()
                {
-                       string defaultName = GetNextDefaultColumnName ();
-                       DataColumn column = new DataColumn (defaultName);
+                       DataColumn column = new DataColumn (null);
                        Add (column);
                        return column;
                }
 
-               private void RegisterName(string name, DataColumn column)
+#if NET_2_0
+               public void CopyTo (DataColumn [] array, int index)
+               {
+                       CopyTo ((Array) array, index);
+               }
+#endif
+
+               internal void RegisterName (string name, DataColumn column)
                {
-                       if (columnFromName.Contains(name))
-                               throw new DuplicateNameException("A DataColumn named '" + name + "' already belongs to this DataTable.");
+                       try {
+                               columnFromName.Add (name, column);
+                       } catch (ArgumentException) {
+                               throw new DuplicateNameException ("A DataColumn named '" + name + "' already belongs to this DataTable.");
+                       }
+
+                       // Get existing doublet
+                       Doublet d = (Doublet) columnNameCount [name];
+                       if (d != null) {
+                               // Add reference count
+                               d.count++;
+                               // Add a new name
+                               d.columnNames.Add (name);
+                       } else {
+                               // no existing doublet
+                               // create one
+                               d = new Doublet (1, name);
+                               columnNameCount [name] = d;
+                       }
 
-                       columnFromName[name] = column;
+#if NET_2_0
+                       if (name.Length <= ColumnPrefix.Length || !name.StartsWith (ColumnPrefix, StringComparison.Ordinal))
+                               return;
+#else
+                       if (name.Length <= ColumnPrefix.Length || !name.StartsWith (ColumnPrefix))
+                               return;
+#endif
 
-                       if (name.StartsWith("Column") && name == MakeName(defaultColumnIndex + 1))
-                       {
-                               do
-                               {
+                       if (name == MakeName (defaultColumnIndex + 1)) {
+                               do {
                                        defaultColumnIndex++;
-                               }
-                               while (Contains(MakeName(defaultColumnIndex + 1)));
+                               } while (Contains (MakeName (defaultColumnIndex + 1)));
                        }
                }
 
-               private void UnregisterName(string name)
+               internal void UnregisterName (string name)
                {
-                       if (columnFromName.Contains(name))
-                               columnFromName.Remove(name);
+                       if (columnFromName.Contains (name))
+                               columnFromName.Remove (name);
+
+                       // Get the existing doublet
+                       Doublet d = (Doublet) columnNameCount [name];
+                       if (d != null) {
+                               // decrease reference count
+                               d.count--;
+                               d.columnNames.Remove (name);
+                               // remove doublet if no more references
+                               if (d.count == 0)
+                                       columnNameCount.Remove (name);
+                       }
 
-                       if (name.StartsWith("Column") && name == MakeName(defaultColumnIndex - 1))
-                       {
-                               do
-                               {
+                       if (name.StartsWith(ColumnPrefix) && name == MakeName(defaultColumnIndex - 1)) {
+                               do {
                                        defaultColumnIndex--;
-                               }
-                               while (!Contains(MakeName(defaultColumnIndex - 1)) && defaultColumnIndex > 1);
+                               } while (!Contains (MakeName (defaultColumnIndex - 1)) && defaultColumnIndex > 1);
                        }
                }
 
                private string GetNextDefaultColumnName ()
                {
-                       string defColumnName = MakeName(defaultColumnIndex);
-                       for (int index = defaultColumnIndex + 1; Contains(defColumnName); ++index) {
-                               defColumnName = MakeName(index);
+                       string defColumnName = MakeName (defaultColumnIndex);
+                       for (int index = defaultColumnIndex + 1; Contains (defColumnName); ++index) {
+                               defColumnName = MakeName (index);
                                defaultColumnIndex++;
                        }
                        defaultColumnIndex++;
@@ -177,26 +247,30 @@ namespace System.Data {
 
                static readonly string[] TenColumns = { "Column0", "Column1", "Column2", "Column3", "Column4", "Column5", "Column6", "Column7", "Column8", "Column9" };
 
-               private string MakeName(int index)
+               static string MakeName (int index)
                {
                        if (index < 10)
-                               return TenColumns[index];
-
-                       return String.Concat("Column", index.ToString());
+                               return TenColumns [index];
+                       return String.Concat (ColumnPrefix, index.ToString());
                }
 
                /// <summary>
                /// Creates and adds the specified DataColumn object to the DataColumnCollection.
                /// </summary>
                /// <param name="column">The DataColumn to add.</param>
-               public void Add(DataColumn column)
+               public void Add (DataColumn column)
                {
-
                        if (column == null)
                                throw new ArgumentNullException ("column", "'column' argument cannot be null.");
 
-                       if (column.ColumnName.Equals(String.Empty))
-                       {
+#if !NET_2_0
+                       /* in 1.1, they must do this here, as the
+                        * setting of ColumnName below causes an event
+                        * to be raised */
+                       column.PropertyChanged += new PropertyChangedEventHandler (ColumnPropertyChanged);
+#endif
+
+                       if (column.ColumnName.Length == 0) {
                                column.ColumnName = GetNextDefaultColumnName ();
                        }
 
@@ -206,18 +280,27 @@ namespace System.Data {
                        if (column.Table != null)
                                throw new ArgumentException ("Column '" + column.ColumnName + "' already belongs to this or another DataTable.");
 
-                       CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
-
                        column.SetTable (parentTable);
-                       RegisterName(column.ColumnName, column);
-                       int ordinal = base.List.Add(column);
-                       column.SetOrdinal (ordinal);
+                       RegisterName (column.ColumnName, column);
+                       int ordinal = base.List.Add (column);
 
-                       // if table already has rows we need to allocate space 
-                       // in the column data container 
-                       if ( parentTable.Rows.Count > 0 ) {
+#if NET_2_0
+                       column.Ordinal = ordinal;
+#else
+                       column.SetOrdinal (ordinal);
+#endif
+
+                       // Check if the Column Expression is ok
+                       if (column.CompiledExpression != null)
+                               if (parentTable.Rows.Count == 0)
+                                       column.CompiledExpression.Eval (parentTable.NewRow());
+                               else
+                                       column.CompiledExpression.Eval (parentTable.Rows[0]);
+
+                       // if table already has rows we need to allocate space
+                       // in the column data container
+                       if (parentTable.Rows.Count > 0)
                                column.DataContainer.Capacity = parentTable.RecordCache.CurrentCapacity;
-                       }
 
                        if (column.AutoIncrement) {
                                DataRowCollection rows = column.Table.Rows;
@@ -226,9 +309,13 @@ namespace System.Data {
                        }
 
                        if (column.AutoIncrement)
-                               autoIncrement.Add(column);
+                               autoIncrement.Add (column);
 
-                       OnCollectionChanged (e);
+#if NET_2_0
+                       column.PropertyChanged += new PropertyChangedEventHandler (ColumnPropertyChanged);
+#endif
+
+                       OnCollectionChanged (new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
                }
 
                /// <summary>
@@ -236,14 +323,13 @@ namespace System.Data {
                /// </summary>
                /// <param name="columnName">The name of the column.</param>
                /// <returns>The newly created DataColumn.</returns>
-               public virtual DataColumn Add(string columnName)
+               public
+#if !NET_2_0
+               virtual
+#endif
+               DataColumn Add (string columnName)
                {
-                       if (columnName == null || columnName == String.Empty)
-                       {
-                               columnName = GetNextDefaultColumnName();
-                       }
-                       
-                       DataColumn column = new DataColumn(columnName);
+                       DataColumn column = new DataColumn (columnName);
                        Add (column);
                        return column;
                }
@@ -254,14 +340,16 @@ namespace System.Data {
                /// <param name="columnName">The ColumnName to use when cretaing the column.</param>
                /// <param name="type">The DataType of the new column.</param>
                /// <returns>The newly created DataColumn.</returns>
-               public virtual DataColumn Add(string columnName, Type type)
+               public
+#if !NET_2_0
+               virtual
+#endif
+               DataColumn Add (string columnName, Type type)
                {
                        if (columnName == null || columnName == "")
-                       {
                                columnName = GetNextDefaultColumnName ();
-                       }
-                       
-                       DataColumn column = new DataColumn(columnName, type);
+
+                       DataColumn column = new DataColumn (columnName, type);
                        Add (column);
                        return column;
                }
@@ -273,14 +361,16 @@ namespace System.Data {
                /// <param name="type">The DataType of the new column.</param>
                /// <param name="expression">The expression to assign to the Expression property.</param>
                /// <returns>The newly created DataColumn.</returns>
-               public virtual DataColumn Add(string columnName, Type type, string expression)
+               public
+#if !NET_2_0
+               virtual
+#endif
+               DataColumn Add (string columnName, Type type, string expression)
                {
                        if (columnName == null || columnName == "")
-                       {
                                columnName = GetNextDefaultColumnName ();
-                       }
-                       
-                       DataColumn column = new DataColumn(columnName, type, expression);
+
+                       DataColumn column = new DataColumn (columnName, type, expression);
                        Add (column);
                        return column;
                }
@@ -289,13 +379,54 @@ namespace System.Data {
                /// Copies the elements of the specified DataColumn array to the end of the collection.
                /// </summary>
                /// <param name="columns">The array of DataColumn objects to add to the collection.</param>
-               public void AddRange(DataColumn[] columns)
+               public void AddRange (DataColumn [] columns)
                {
-                       foreach (DataColumn column in columns)
-                       {
+                       if (parentTable.InitInProgress){
+                               _mostRecentColumns = columns;
+                               return;
+                       }
+
+                       if (columns == null)
+                               return;
+
+                       foreach (DataColumn column in columns){
+                               if (column == null)
+                                       continue;
                                Add(column);
                        }
-                       return;
+               }
+
+               private string GetColumnDependency (DataColumn column)
+               {
+
+                       foreach (DataRelation rel in parentTable.ParentRelations)
+                               if (Array.IndexOf (rel.ChildColumns, column) != -1)
+                                       return String.Format (" child key for relationship {0}.", rel.RelationName);
+                       foreach (DataRelation rel in parentTable.ChildRelations)
+                               if (Array.IndexOf (rel.ParentColumns, column) != -1)
+                                       return String.Format (" parent key for relationship {0}.", rel.RelationName);
+
+                       foreach (Constraint c in parentTable.Constraints)
+                               if (c.IsColumnContained (column))
+                                       return String.Format (" constraint {0} on the table {1}.",
+                                                       c.ConstraintName, parentTable);
+
+
+                       // check if the foreign-key constraint on any table in the dataset refers to this column.
+                       // though a forignkeyconstraint automatically creates a uniquecontrainton the parent
+                       // table and would fail above, but we still need to check, as it is legal to manually remove
+                       // the constraint on the parent table.
+                       if (parentTable.DataSet != null)
+                               foreach (DataTable table in parentTable.DataSet.Tables)
+                                       foreach (Constraint c in table.Constraints)
+                                               if (c is ForeignKeyConstraint && c.IsColumnContained(column))
+                                                       return String.Format (
+                                                               " constraint {0} on the table {1}.", c.ConstraintName, table.TableName);
+
+                       foreach (DataColumn col in this)
+                               if (col.CompiledExpression != null && col.CompiledExpression.DependsOn (column))
+                                       return  col.Expression;
+                       return String.Empty;
                }
 
                /// <summary>
@@ -303,168 +434,55 @@ namespace System.Data {
                /// </summary>
                /// <param name="column">A DataColumn in the collection.</param>
                /// <returns>true if the column can be removed; otherwise, false.</returns>
-               public bool CanRemove(DataColumn column)
+               public bool CanRemove (DataColumn column)
                {
-                                               
-                       //Check that the column does not have a null reference.
-                       if (column == null)
-                                return false;
-
-                       
-                       //Check that the column is part of this collection.
-                       if (!Contains(column.ColumnName))
-                       {
+                       if (column == null || column.Table != parentTable || GetColumnDependency (column) != String.Empty)
                                return false;
-                       }
-
-
-                       
-                       //Check if this column is part of a relationship. (this could probably be written better)
-                       foreach (DataRelation childRelation in parentTable.ChildRelations)
-                       {
-                               foreach (DataColumn childColumn in childRelation.ChildColumns)
-                               {
-                                       if (childColumn == column)
-                                       {
-                                               return false;
-                                       }
-                               }
-
-                               foreach (DataColumn parentColumn in childRelation.ParentColumns)
-                               {
-                                       if (parentColumn == column)
-                                       {
-                                               return false;
-                                       }
-                               }
-                       }
-
-                       //Check if this column is part of a relationship. (this could probably be written better)
-                       foreach (DataRelation parentRelation in parentTable.ParentRelations)
-                       {
-                               foreach (DataColumn childColumn in parentRelation.ChildColumns)
-                               {
-                                       if (childColumn == column)
-                                       {
-                                               return false;
-                                       }
-                               }
-
-                               foreach (DataColumn parentColumn in parentRelation.ParentColumns)
-                               {
-                                       if (parentColumn == column)
-                                       {
-                                               return false;
-                                       }
-                               }
-                       }
-
-                       
-                       //Check if another column's expression depends on this column.
-                       
-                       foreach (DataColumn dataColumn in List)
-                       {
-                               if (dataColumn.Expression.ToString().IndexOf(column.ColumnName) > 0)
-                               {
-                                       return false;
-                               }
-                       }
-                       
-                        // check for part of pk
-                        UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint (parentTable.Constraints);
-                        if (uc != null && uc.Contains (column)) 
-                                throw new ArgumentException (String.Format ("Cannot remove column {0}, because" +
-                                                             " it is part of primarykey",
-                                                             column.ColumnName));
-                        // check for part of fk
-                        DataSet ds = parentTable.DataSet;
-                        
-                        if (ds != null) {
-                                foreach (DataTable t in ds.Tables) {
-                                        if (t == parentTable)
-                                                continue;
-                                        foreach (Constraint c in t.Constraints) {
-                                                if (! (c is ForeignKeyConstraint))
-                                                        continue;
-                                                ForeignKeyConstraint fk = (ForeignKeyConstraint) c;
-                                                if (fk.Contains (column, true)      // look in parent
-                                                    || fk.Contains (column, false)) // look in children
-                                                        throw new ArgumentException (String.Format ("Cannot remove column {0}, because" +
-                                                                                     " it is part of foreign key constraint",
-                                                                                     column.ColumnName));
-                                                
-                                        }
-                                        
-                                }
-                                
-                                
-                        }
-                        
                        return true;
                }
 
                /// <summary>
                /// Clears the collection of any columns.
                /// </summary>
-               public void Clear()
+               public void Clear ()
                {
                        CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this);
 
-
-                       // FIXME: Hmm... This loop could look little nicer :)
-                       foreach (DataColumn Col in List) {
-
-                               foreach (DataRelation Rel in Col.Table.ParentRelations) {
-
-                                       foreach (DataColumn Col2 in Rel.ParentColumns) {
-                                               if (Object.ReferenceEquals (Col, Col2))
-                                                       throw new ArgumentException ("Cannot remove this column, because " + 
-                                                                                    "it is part of the parent key for relationship " + 
-                                                                                    Rel.RelationName + ".");
-                                       }
-
-                                       foreach (DataColumn Col2 in Rel.ChildColumns) {
-                                               if (Object.ReferenceEquals (Col, Col2))
-                                                       throw new ArgumentException ("Cannot remove this column, because " + 
-                                                                                    "it is part of the parent key for relationship " + 
-                                                                                    Rel.RelationName + ".");
-
-                                       }
+                       // its not necessary to check if each column in the collection can removed.
+                       // Can simply check, if there are any constraints/relations related to the table,
+                       // in which case, throw an exception.
+                       // Also, shudnt check for expression columns since all the columns in the table
+                       // are being removed.
+                       if (parentTable.Constraints.Count != 0 ||
+                           parentTable.ParentRelations.Count != 0 ||
+                           parentTable.ChildRelations.Count != 0)
+                               foreach (DataColumn col in this) {
+                                       string s = GetColumnDependency (col);
+                                       if (s != String.Empty)
+                                               throw new ArgumentException ("Cannot remove this column, because it is part of the" + s);
                                }
 
-                               foreach (DataRelation Rel in Col.Table.ChildRelations) {
-
-                                       foreach (DataColumn Col2 in Rel.ParentColumns) {
-                                               if (Object.ReferenceEquals (Col, Col2))
-                                                       throw new ArgumentException ("Cannot remove this column, because " + 
-                                                                                    "it is part of the parent key for relationship " + 
-                                                                                    Rel.RelationName + ".");
-                                       }
-
-                                       foreach (DataColumn Col2 in Rel.ChildColumns) {
-                                               if (Object.ReferenceEquals (Col, Col2))
-                                                       throw new ArgumentException ("Cannot remove this column, because " + 
-                                                                                    "it is part of the parent key for relationship " + 
-                                                                                    Rel.RelationName + ".");
+                       if (parentTable.DataSet != null)
+                               foreach (DataTable table in parentTable.DataSet.Tables)
+                                       foreach (Constraint c in table.Constraints) {
+                                               if (!(c is ForeignKeyConstraint) ||
+                                                   ((ForeignKeyConstraint) c).RelatedTable != parentTable)
+                                                       continue;
+                                               throw new ArgumentException (
+                                                       String.Format (
+                                                               "Cannot remove this column, because it is part of the constraint {0} on the table {1}",
+                                                               c.ConstraintName, table.TableName));
                                        }
-                               }
-
-                       }
 
-                        // whether all columns can be removed
-                        foreach (DataColumn col in this) {
-                                if (!CanRemove (col))
-                                        throw new ArgumentException ("Cannot remove column {0}", col.ColumnName);
-                        }
+                       foreach (DataColumn col in this)
+                               col.ResetColumnInfo ();
 
-                       try {
-                               columnFromName.Clear();
-                               autoIncrement.Clear();
-                               base.List.Clear();
-                               OnCollectionChanged(e);
-                       } catch (Exception ex) {
-                               throw new ArgumentException (ex.Message, ex);
-                       }
+                       columnFromName.Clear ();
+                       autoIncrement.Clear ();
+                       columnNameCount.Clear ();
+                       base.List.Clear ();
+                       defaultColumnIndex = 1;
+                       OnCollectionChanged (e);
                }
 
                /// <summary>
@@ -472,12 +490,12 @@ namespace System.Data {
                /// </summary>
                /// <param name="name">The ColumnName of the column to check for.</param>
                /// <returns>true if a column exists with this name; otherwise, false.</returns>
-               public bool Contains(string name)
+               public bool Contains (string name)
                {
-                       if (columnFromName.Contains(name))
+                       if (columnFromName.Contains (name))
                                return true;
-                       
-                       return (IndexOf(name, false) != -1);
+
+                       return (IndexOf (name, false) != -1);
                }
 
                /// <summary>
@@ -485,9 +503,15 @@ namespace System.Data {
                /// </summary>
                /// <param name="column">The name of the column to return.</param>
                /// <returns>The index of the column specified by column if it is found; otherwise, -1.</returns>
-               public virtual int IndexOf(DataColumn column)
+               public
+#if !NET_2_0
+               virtual
+#endif
+               int IndexOf (DataColumn column)
                {
-                       return base.List.IndexOf(column);
+                       if (column == null)
+                               return -1;
+                       return base.List.IndexOf (column);
                }
 
                /// <summary>
@@ -495,36 +519,46 @@ namespace System.Data {
                /// </summary>
                /// <param name="columnName">The name of the column to find.</param>
                /// <returns>The zero-based index of the column with the specified name, or -1 if the column doesn't exist in the collection.</returns>
-               public int IndexOf(string columnName)
+               public int IndexOf (string columnName)
                {
-                       DataColumn dc = columnFromName[columnName] as DataColumn;
-                               
+                       if (columnName == null)
+                               return -1;
+                       DataColumn dc = columnFromName [columnName] as DataColumn;
+
                        if (dc != null)
-                               return IndexOf(dc);
+                               return IndexOf (dc);
 
-                       return IndexOf(columnName, false);
+                       return IndexOf (columnName, false);
                }
 
                /// <summary>
                /// Raises the OnCollectionChanged event.
                /// </summary>
                /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
-               protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent)
+#if !NET_2_0
+               protected virtual
+#else
+               internal
+#endif
+               void OnCollectionChanged (CollectionChangeEventArgs ccevent)
                {
-                       if (CollectionChanged != null) 
-                       {
-                               CollectionChanged(this, ccevent);
-                       }
+                       parentTable.ResetPropertyDescriptorsCache ();
+                       if (CollectionChanged != null)
+                               CollectionChanged (this, ccevent);
                }
 
                /// <summary>
                /// Raises the OnCollectionChanging event.
                /// </summary>
                /// <param name="ccevent">A CollectionChangeEventArgs that contains the event data.</param>
-               protected internal virtual void OnCollectionChanging(CollectionChangeEventArgs ccevent)
+#if !NET_2_0
+               protected internal virtual
+#else
+               internal
+#endif
+               void OnCollectionChanging (CollectionChangeEventArgs ccevent)
                {
-                       if (CollectionChanged != null) 
-                       {
+                       if (CollectionChanged != null) {
                                //FIXME: this is not right
                                //CollectionChanged(this, ccevent);
                                throw new NotImplementedException();
@@ -535,120 +569,160 @@ namespace System.Data {
                /// Removes the specified DataColumn object from the collection.
                /// </summary>
                /// <param name="column">The DataColumn to remove.</param>
-               public void Remove(DataColumn column)
+               public void Remove (DataColumn column)
                {
                        if (column == null)
                                throw new ArgumentNullException ("column", "'column' argument cannot be null.");
 
-                       if (!Contains(column.ColumnName))
+                       if (!Contains (column.ColumnName))
                                throw new ArgumentException ("Cannot remove a column that doesn't belong to this table.");
-                       //TODO: can remove first with exceptions
-                       //and OnChanging Event
-                       CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Remove, this);
-                       
+
+                       string dependency = GetColumnDependency (column);
+                       if (dependency != String.Empty)
+                               throw new ArgumentException ("Cannot remove this column, because it is part of " + dependency);
+
+                       CollectionChangeEventArgs e = new CollectionChangeEventArgs (CollectionChangeAction.Remove, column);
+
                        int ordinal = column.Ordinal;
-                       UnregisterName(column.ColumnName);
-                       base.List.Remove(column);
-                       
+                       UnregisterName (column.ColumnName);
+                       base.List.Remove (column);
+
+                       // Reset column info
+                       column.ResetColumnInfo ();
+
                        //Update the ordinals
                        for( int i = ordinal ; i < this.Count ; i ++ )
-                       {
-                               this[i].SetOrdinal( i );
-                       }
+#if NET_2_0
+                               this[i].Ordinal = i;
+#else
+                               this[i].SetOrdinal(i);
+#endif
 
                        if (parentTable != null)
-                               parentTable.OnRemoveColumn(column);
+                               parentTable.OnRemoveColumn (column);
 
                        if (column.AutoIncrement)
-                               autoIncrement.Remove(column);
+                               autoIncrement.Remove (column);
 
-                       OnCollectionChanged(e);
-                       return;
+                       column.PropertyChanged -= new PropertyChangedEventHandler (ColumnPropertyChanged);
+
+                       OnCollectionChanged (e);
                }
 
                /// <summary>
                /// Removes the DataColumn object with the specified name from the collection.
                /// </summary>
                /// <param name="name">The name of the column to remove.</param>
-               public void Remove(string name)
+               public void Remove (string name)
                {
-                       DataColumn column = this[name];
-                       
+                       DataColumn column = this [name];
+
                        if (column == null)
                                throw new ArgumentException ("Column '" + name + "' does not belong to table " + ( parentTable == null ? "" : parentTable.TableName ) + ".");
-                       Remove(column);
+                       Remove (column);
                }
 
                /// <summary>
                /// Removes the column at the specified index from the collection.
                /// </summary>
                /// <param name="index">The index of the column to remove.</param>
-               public void RemoveAt(int index)
+               public void RemoveAt (int index)
                {
                        if (Count <= index)
                                throw new IndexOutOfRangeException ("Cannot find column " + index + ".");
 
-                       DataColumn column = this[index];
-                       Remove(column);
+                       DataColumn column = this [index];
+                       Remove (column);
                }
 
-
-               /// <summary>
-               ///  Do the same as Constains -method but case sensitive
-               /// </summary>
-               private bool CaseSensitiveContains(string columnName)
+               // Helper AddRange() - Call this function when EndInit is called
+               internal void PostAddRange ()
                {
-                       DataColumn column = this[columnName];
-                       
-                       if (column != null)
-                               return string.Compare(column.ColumnName, columnName, false) == 0; 
+                       if (_mostRecentColumns == null)
+                               return;
 
-                       return false;
+                       foreach (DataColumn column in _mostRecentColumns){
+                               if (column == null)
+                                       continue;
+                               Add (column);
+                       }
+                       _mostRecentColumns = null;
                }
 
-               internal void UpdateAutoIncrement(DataColumn col,bool isAutoIncrement)
+               internal void UpdateAutoIncrement (DataColumn col,bool isAutoIncrement)
                {
-                       if (isAutoIncrement)
-                       {
-                               if (!autoIncrement.Contains(col))
-                                       autoIncrement.Add(col);
-                       }
-                       else
-                       {
-                               if (autoIncrement.Contains(col))
-                                       autoIncrement.Remove(col);
+                       if (isAutoIncrement) {
+                               if (!autoIncrement.Contains (col))
+                                       autoIncrement.Add (col);
+                       } else {
+                               if (autoIncrement.Contains (col))
+                                       autoIncrement.Remove (col);
                        }
                }
 
                private int IndexOf (string name, bool error)
                {
-                       int count = 0, match = -1;
-                       for (int i = 0; i < List.Count; i++)
-                       {
-                               String name2 = ((DataColumn) List[i]).ColumnName;
-                               if (String.Compare (name, name2, true) == 0)
-                               {
-                                       if (String.Compare (name, name2, false) == 0)
-                                               return i;
-                                       match = i;
-                                       count++;
+                       // exact case matching has already be done by the caller
+                       // Get existing doublet
+                       Doublet d = (Doublet) columnNameCount [name];
+                       if (d != null) {
+                               if (d.count == 1) {
+                                       // There's only one
+                                       // return index of the column from the only column name of the doublet
+                                       return base.List.IndexOf (columnFromName [d.columnNames [0]]);
+                               } else if (d.count > 1 && error) {
+                                       // there's more than one, exception!
+                                       throw new ArgumentException ("There is no match for '" + name + "' in the same case and there are multiple matches in different case.");
+                               } else {
+                                       return -1;
                                }
                        }
-                       if (count == 1)
-                               return match;
-                       if (count > 1 && error)
-                               throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
                        return -1;
                }
-               
+
                #region Events
 
                /// <summary>
                /// Occurs when the columns collection changes, either by adding or removing a column.
                /// </summary>
-                [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")] 
+               [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
                public event CollectionChangeEventHandler CollectionChanged;
 
-               #endregion 
+               internal event CollectionChangeEventHandler CollectionMetaDataChanged;
+               #endregion
+
+               private void OnCollectionMetaDataChanged (CollectionChangeEventArgs ccevent)
+               {
+                       parentTable.ResetPropertyDescriptorsCache ();
+                       if (CollectionMetaDataChanged != null)
+                               CollectionMetaDataChanged (this, ccevent);
+               }
+
+               private void ColumnPropertyChanged (object sender, PropertyChangedEventArgs args)
+               {
+                       OnCollectionMetaDataChanged (new CollectionChangeEventArgs(CollectionChangeAction.Refresh, sender));
+               }
+
+#if NET_2_0
+               internal void MoveColumn (int oldOrdinal, int newOrdinal)
+               {
+                       if (newOrdinal == -1 || newOrdinal > this.Count)
+                               throw new ArgumentOutOfRangeException ("ordinal", "Ordinal '" + newOrdinal + "' exceeds the maximum number.");
+                       if (oldOrdinal == newOrdinal)
+                               return;
+
+                       int start = newOrdinal > oldOrdinal ? oldOrdinal : newOrdinal;
+                       int end = newOrdinal > oldOrdinal ?  newOrdinal : oldOrdinal;
+                       int direction = newOrdinal > oldOrdinal ? 1 : (-1);
+
+                       DataColumn currColumn = this [start];
+                       for (int i = start; i < end; i += direction) {
+                               List [i] = List [i+direction];
+                               ((DataColumn) List [i]).Ordinal = i;
+                       }
+                       List [end] = currColumn;
+                       currColumn.Ordinal = end;
+               }
+#endif
        }
 }