X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Data%2FSystem.Data%2FDataColumnCollection.cs;h=5c8bae2d75445c85b857986ab396b34d68a1683d;hb=ac27105c704380af358b4c5da005d4efc1e513f4;hp=9e7fae7704dd30dbca8311bb969d73a6c1d69537;hpb=c381db50a2e5a6cefc0f0e95e5cec698e91ce8fa;p=mono.git diff --git a/mcs/class/System.Data/System.Data/DataColumnCollection.cs b/mcs/class/System.Data/System.Data/DataColumnCollection.cs index 9e7fae7704d..5c8bae2d754 100644 --- a/mcs/class/System.Data/System.Data/DataColumnCollection.cs +++ b/mcs/class/System.Data/System.Data/DataColumnCollection.cs @@ -4,32 +4,61 @@ // Author: // Christopher Podurgiel (cpodurgiel@msn.com) // Stuart Caborn +// Tim Coleman (tim@timcoleman.com) // // (C) Chris Podurgiel +// Copyright (C) Tim Coleman, 2002 +// Copyright (C) Daniel Morgan, 2003 +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// 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 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.ComponentModel; -namespace System.Data -{ - /// - /// Represents a collection of DataColumn objects for a DataTable. - /// +namespace System.Data { + [Editor] [Serializable] + [DefaultEvent ("CollectionChanged")] public class DataColumnCollection : InternalDataCollectionBase { - // The defaultNameIndex is used to create a default name for a column if one wasn't given. - private int defaultNameIndex; - + //This hashtable maps between column name to DataColumn object. + private Hashtable columnFromName = new Hashtable(); + //This ArrayList contains the auto-increment columns names + 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; // Internal Constructor. This Class can only be created from other classes in this assembly. internal DataColumnCollection(DataTable table):base() { - defaultNameIndex = 1; parentTable = table; } @@ -40,6 +69,9 @@ namespace System.Data { get { + if (index < 0 || index > base.List.Count) { + throw new IndexOutOfRangeException("Cannot find column " + index + "."); + } return (DataColumn) base.List[index]; } } @@ -51,21 +83,22 @@ namespace System.Data { get { - foreach (DataColumn column in base.List) - { - if (column.ColumnName == name) - { - return column; - } - } - return null; + DataColumn dc = columnFromName[name] as DataColumn; + + if (dc != null) + return dc; + + int tmp = IndexOf(name, true); + if (tmp == -1) + return null; + return this[tmp]; } } /// /// Gets a list of the DataColumnCollection items. /// - protected internal override ArrayList List + protected override ArrayList List { get { @@ -73,6 +106,14 @@ namespace System.Data } } + internal ArrayList AutoIncrmentColumns + { + get + { + return autoIncrement; + } + } + //Add Logic // //Changing Event @@ -91,49 +132,109 @@ namespace System.Data /// public virtual DataColumn Add() { - //FIXME: - DataColumn column = new DataColumn("Column" + defaultNameIndex.ToString()); - CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this); - - column.SetTable(parentTable); - base.List.Add(column); - OnCollectionChanged(e); - defaultNameIndex++; + string defaultName = GetNextDefaultColumnName (); + DataColumn column = new DataColumn (defaultName); + Add (column); return column; } + internal void RegisterName(string name, DataColumn column) + { + if (columnFromName.Contains(name)) + throw new DuplicateNameException("A DataColumn named '" + name + "' already belongs to this DataTable."); + + columnFromName[name] = column; + + if (name.StartsWith("Column") && name == MakeName(defaultColumnIndex + 1)) + { + do + { + defaultColumnIndex++; + } + while (Contains(MakeName(defaultColumnIndex + 1))); + } + } + + internal void UnregisterName(string name) + { + if (columnFromName.Contains(name)) + columnFromName.Remove(name); + + if (name.StartsWith("Column") && name == MakeName(defaultColumnIndex - 1)) + { + do + { + defaultColumnIndex--; + } + 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); + defaultColumnIndex++; + } + defaultColumnIndex++; + return defColumnName; + } + + static readonly string[] TenColumns = { "Column0", "Column1", "Column2", "Column3", "Column4", "Column5", "Column6", "Column7", "Column8", "Column9" }; + + private string MakeName(int index) + { + if (index < 10) + return TenColumns[index]; + + return String.Concat("Column", index.ToString()); + } + /// /// Creates and adds the specified DataColumn object to the DataColumnCollection. /// /// The DataColumn to add. - [MonoTODO] public void Add(DataColumn column) - { - //FIXME: - if(Contains(column.ColumnName)) + { + + if (column == null) + throw new ArgumentNullException ("column", "'column' argument cannot be null."); + + if (column.ColumnName.Equals(String.Empty)) { - throw new DuplicateNameException("A column named " + column.ColumnName + " already belongs to this DataTable."); + column.ColumnName = GetNextDefaultColumnName (); } - else - { - CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this); - - column.SetTable( parentTable); - base.List.Add(column); - - //add constraints if neccesary - if(column.Unique) - { - UniqueConstraint uc = new UniqueConstraint(column); - parentTable.Constraints.Add(uc); - } - - //TODO: add missing constraints. i.e. Primary/Foreign keys +// if (Contains(column.ColumnName)) +// throw new DuplicateNameException("A DataColumn named '" + column.ColumnName + "' already belongs to this DataTable."); - OnCollectionChanged(e); - return; + 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); + + // 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; + for (int i = 0; i < rows.Count; i++) + rows [i] [ordinal] = column.AutoIncrementValue (); } + + if (column.AutoIncrement) + autoIncrement.Add(column); + + OnCollectionChanged (e); } /// @@ -143,30 +244,14 @@ namespace System.Data /// The newly created DataColumn. public virtual DataColumn Add(string columnName) { - - //FIXME: this wont work. If the user decides to add a column named - //"ColumnXX" where XX is a number these two will conflict. if (columnName == null || columnName == String.Empty) { - columnName = "Column" + defaultNameIndex.ToString(); - defaultNameIndex++; + columnName = GetNextDefaultColumnName(); } - if(Contains(columnName)) - { - throw new DuplicateNameException("A column named " + columnName + " already belongs to this DataTable."); - } - else - { - DataColumn column = new DataColumn(columnName); - - CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this); - column.SetTable(parentTable); - int ordinal = base.List.Add(column); - column.SetOrdinal( ordinal ); - OnCollectionChanged(e); - return column; - } + DataColumn column = new DataColumn(columnName); + Add (column); + return column; } /// @@ -179,26 +264,12 @@ namespace System.Data { if (columnName == null || columnName == "") { - //FIXME: this wont work. If the user decides to add a column named - //"ColumnXX" where XX is a number these two will conflict. - columnName = "Column" + defaultNameIndex.ToString(); - defaultNameIndex++; - } - - if(Contains(columnName)) - { - throw new DuplicateNameException("A column named " + columnName + " already belongs to this DataTable."); - } - else - { - DataColumn column = new DataColumn(columnName, type); - CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this); - column.SetTable(parentTable); - int ordinal = base.List.Add(column); - column.SetOrdinal( ordinal ); - OnCollectionChanged(e); - return column; + columnName = GetNextDefaultColumnName (); } + + DataColumn column = new DataColumn(columnName, type); + Add (column); + return column; } /// @@ -208,29 +279,16 @@ namespace System.Data /// The DataType of the new column. /// The expression to assign to the Expression property. /// The newly created DataColumn. - public virtual DataColumn Add(string columnName, Type type, string expression) + public virtual DataColumn Add(string columnName, Type type, string expression) { - //FIXME: See Add Logic if (columnName == null || columnName == "") { - columnName = "Column" + defaultNameIndex.ToString(); - defaultNameIndex++; + columnName = GetNextDefaultColumnName (); } - if(Contains(columnName)) - { - throw new DuplicateNameException("A column named " + columnName + " already belongs to this DataTable."); - } - else - { - DataColumn column = new DataColumn(columnName, type, expression); - CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this); - column.SetTable(parentTable); - int ordinal = base.List.Add(column); - column.SetOrdinal( ordinal ); - OnCollectionChanged(e); - return column; - } + DataColumn column = new DataColumn(columnName, type, expression); + Add (column); + return column; } /// @@ -239,11 +297,18 @@ namespace System.Data /// The array of DataColumn objects to add to the collection. public void AddRange(DataColumn[] columns) { + if (parentTable.fInitInProgress){ + _mostRecentColumns = columns; + return; + } + + if (columns == null) + return; + foreach (DataColumn column in columns) { Add(column); } - return; } /// @@ -255,34 +320,38 @@ namespace System.Data { //Check that the column does not have a null reference. - if (column == null) + if (column == null) { return false; } //Check that the column is part of this collection. - if (!Contains(column.ColumnName)) + if (parentTable != column.Table) { return false; } + parentTable.OnRemoveColumn(column); + UniqueConstraint primaryKey = parentTable.PrimaryKeyConstraint; + if (primaryKey != null && primaryKey.IsColumnContained(column)) + 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) + foreach (DataColumn childColumn in childRelation.ChildColumns) { - if (childColumn == column) + if (childColumn == column) { return false; } } - foreach (DataColumn parentColumn in childRelation.ParentColumns) + foreach (DataColumn parentColumn in childRelation.ParentColumns) { - if (parentColumn == column) + if (parentColumn == column) { return false; } @@ -290,38 +359,67 @@ namespace System.Data } //Check if this column is part of a relationship. (this could probably be written better) - foreach (DataRelation parentRelation in parentTable.ParentRelations) + foreach (DataRelation parentRelation in parentTable.ParentRelations) { - foreach (DataColumn childColumn in parentRelation.ChildColumns) + foreach (DataColumn childColumn in parentRelation.ChildColumns) { - if (childColumn == column) + if (childColumn == column) { return false; } } - foreach (DataColumn parentColumn in parentRelation.ParentColumns) + foreach (DataColumn parentColumn in parentRelation.ParentColumns) { - if (parentColumn == column) + if (parentColumn == column) { return false; } } } + //TODO: check constraints + for (int i = 0; i < parentTable.Constraints.Count; i++) { + if (parentTable.Constraints[i].IsColumnContained(column)) + return false; + } + + if (parentTable.DataSet != null) { + //FIXME: check whether some parent key in ForeignConstriants contains + // the column + } + //Check if another column's expression depends on this column. - foreach (DataColumn dataColumn in List) + foreach (DataColumn dataColumn in List) { - if (dataColumn.Expression.ToString().IndexOf(column.ColumnName) > 0) + if (dataColumn.CompiledExpression != null && + dataColumn.CompiledExpression.DependsOn(column)) { return false; } } - - //TODO: check constraints - + // check for part of pk + UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint (parentTable.Constraints); + if (uc != null && uc.IsColumnContained(column)) { + return false; + } + // 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.IsColumnContained(column)) + return false; + } + } + } return true; } @@ -331,9 +429,52 @@ namespace System.Data 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 + "."); + + } + } + + 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 + "."); + } + } + } + + // whether all columns can be removed + foreach (DataColumn col in this) { + if (!CanRemove (col)) + throw new ArgumentException ("Cannot remove column {0}", col.ColumnName); + } + + columnFromName.Clear(); + autoIncrement.Clear(); base.List.Clear(); OnCollectionChanged(e); - return; + } /// @@ -343,7 +484,10 @@ namespace System.Data /// true if a column exists with this name; otherwise, false. public bool Contains(string name) { - return (IndexOf(name) != -1); + if (columnFromName.Contains(name)) + return true; + + return (IndexOf(name, false) != -1); } /// @@ -363,17 +507,12 @@ namespace System.Data /// The zero-based index of the column with the specified name, or -1 if the column doesn't exist in the collection. public int IndexOf(string columnName) { - - DataColumn column = this[columnName]; - - if (column != null) - { - return IndexOf(column); - } - else - { - return -1; - } + DataColumn dc = columnFromName[columnName] as DataColumn; + + if (dc != null) + return IndexOf(dc); + + return IndexOf(columnName, false); } /// @@ -382,6 +521,7 @@ namespace System.Data /// A CollectionChangeEventArgs that contains the event data. protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent) { + parentTable.ResetPropertyDescriptorsCache(); if (CollectionChanged != null) { CollectionChanged(this, ccevent); @@ -408,11 +548,17 @@ namespace System.Data /// The DataColumn to remove. public void Remove(DataColumn column) { + if (column == null) + throw new ArgumentNullException ("column", "'column' argument cannot be null."); + + 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); int ordinal = column.Ordinal; + UnregisterName(column.ColumnName); base.List.Remove(column); //Update the ordinals @@ -420,7 +566,13 @@ namespace System.Data { this[i].SetOrdinal( i ); } - + + if (parentTable != null) + parentTable.OnRemoveColumn(column); + + if (column.AutoIncrement) + autoIncrement.Remove(column); + OnCollectionChanged(e); return; } @@ -431,8 +583,11 @@ namespace System.Data /// The name of the column to remove. public void Remove(string name) { - DataColumn column = this[name]; - Remove( column ); + DataColumn column = this[name]; + + if (column == null) + throw new ArgumentException ("Column '" + name + "' does not belong to table " + ( parentTable == null ? "" : parentTable.TableName ) + "."); + Remove(column); } /// @@ -441,13 +596,77 @@ namespace System.Data /// The index of the column to remove. public void RemoveAt(int index) { + if (Count <= index) + throw new IndexOutOfRangeException ("Cannot find column " + index + "."); + DataColumn column = this[index]; - Remove( column ); + Remove(column); + } + + // Helper AddRange() - Call this function when EndInit is called + internal void PostEndInit() { + DataColumn[] cols = _mostRecentColumns; + _mostRecentColumns = null; + AddRange (cols); } + + /// + /// Do the same as Constains -method but case sensitive + /// + private bool CaseSensitiveContains(string columnName) + { + DataColumn column = this[columnName]; + + if (column != null) + return string.Compare(column.ColumnName, columnName, false) == 0; + + return false; + } + + internal void UpdateAutoIncrement(DataColumn col,bool isAutoIncrement) + { + 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++; + } + } + 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 + /// /// Occurs when the columns collection changes, either by adding or removing a column. /// + [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")] public event CollectionChangeEventHandler CollectionChanged; + + #endregion } }