Переглянути джерело

* DataRow.cs (CheckChildRows) : Improving the implementation - checking child for all FK, and not
on all Relations.
(GetChildRows) : Adding some checks.
(GetParentRows) : Adding some checks. Fix a bug in implementation.
(SetParentRow) : Added implementation.

* DataRowCollection.cs (Add) : Added more checks on the row added to the collection.
* DataTable.cs (Copy) : Set the During Loading flag to save unnecessary assertions.
* ForeignKeyConstraint.cs (AssertConstraint) : Fixing bugs in implementation. Checking for DBNull values in the row.
* MergeManager.cs : Check that the target table is not null.

svn path=/trunk/mcs/; revision=19640

Eran Domb 22 роки тому
батько
коміт
1ad45b3cb7

+ 12 - 0
mcs/class/System.Data/System.Data/ChangeLog

@@ -1,3 +1,15 @@
+2003-11-04  Eran Domb  <[email protected]>
+	* DataRow.cs (CheckChildRows) : Improving the implementation - checking child for all FK, and not
+	on all Relations.
+	(GetChildRows) : Adding some checks.
+	(GetParentRows) : Adding some checks. Fix a bug in implementation.
+	(SetParentRow) : Added implementation.
+	
+	* DataRowCollection.cs (Add) : Added more checks on the row added to the collection.
+	* DataTable.cs (Copy) : Set the During Loading flag to save unnecessary assertions.
+	* ForeignKeyConstraint.cs (AssertConstraint) : Fixing bugs in implementation. Checking for DBNull values in the row.
+	* MergeManager.cs : Check that the target table is not null.
+
 2003-10-27  Eran Domb  <[email protected]>
 	* DataColumn.cs (DefaultValue) : Changing null value to DBNull. Checking that the type of the new default value 
 	can be converted to the column type.

+ 1212 - 1118
mcs/class/System.Data/System.Data/DataRow.cs

@@ -1,1118 +1,1212 @@
-//
-// System.Data.DataRow.cs
-//
-// Author:
-//   Rodrigo Moya <[email protected]>
-//   Daniel Morgan <[email protected]>
-//   Tim Coleman <[email protected]>
-//   Ville Palo <[email protected]>
-//   Alan Tam Siu Lung <[email protected]>
-//
-// (C) Ximian, Inc 2002
-// (C) Daniel Morgan 2002, 2003
-// Copyright (C) 2002 Tim Coleman
-//
-
-using System;
-using System.Collections;
-using System.Globalization;
-
-namespace System.Data {
-	/// <summary>
-	/// Represents a row of data in a DataTable.
-	/// </summary>
-	[Serializable]
-	public class DataRow
-	{
-		#region Fields
-
-		private DataTable _table;
-
-		private object[] original;
-		private object[] proposed;
-		private object[] current;
-
-		private string[] columnErrors;
-		private string rowError;
-		private DataRowState rowState;
-		internal int xmlRowID = 0;
-		internal bool _nullConstraintViolation;
-		private bool editing = false;
-
-		#endregion
-
-		#region Constructors
-
-		/// <summary>
-		/// This member supports the .NET Framework infrastructure and is not intended to be 
-		/// used directly from your code.
-		/// </summary>
-		protected internal DataRow (DataRowBuilder builder)
-		{
-			_table = builder.Table;
-
-			original = null; 
-			
-			proposed = new object[_table.Columns.Count];
-			for (int c = 0; c < _table.Columns.Count; c++) 
-			{
-				proposed[c] = DBNull.Value;
-			}
-			
-			columnErrors = new string[_table.Columns.Count];
-			rowError = String.Empty;
-
-			//on first creating a DataRow it is always detached.
-			rowState = DataRowState.Detached;
-
-			foreach (DataColumn Col in _table.Columns) {
-				
-				if (Col.AutoIncrement) {
-					this [Col] = Col.AutoIncrementValue();
-				}
-			}
-
-			_table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
-		}
-
-		
-		#endregion
-
-		#region Properties
-
-		/// <summary>
-		/// Gets a value indicating whether there are errors in a row.
-		/// </summary>
-		public bool HasErrors {
-			[MonoTODO]
-			get {
-				if (RowError != string.Empty)
-					return true;
-
-				for (int i= 0; i < columnErrors.Length; i++){
-					if (columnErrors[i] != null && columnErrors[i] != string.Empty)
-						return true;
-				}
-
-				return false;
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the data stored in the column specified by name.
-		/// </summary>
-		public object this[string columnName] {
-			get { return this[columnName, DataRowVersion.Default]; }
-			set {
-				int columnIndex = _table.Columns.IndexOf (columnName);
-				if (columnIndex == -1)
-					throw new IndexOutOfRangeException ();
-				this[columnIndex] = value;
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the data stored in specified DataColumn
-		/// </summary>
-		public object this[DataColumn column] {
-
-			get {
-				return this[column, DataRowVersion.Default];} 
-			set {
-				int columnIndex = _table.Columns.IndexOf (column);
-				if (columnIndex == -1)
-					throw new ArgumentException ("The column does not belong to this table.");
-				this[columnIndex] = value;
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the data stored in column specified by index.
-		/// </summary>
-		public object this[int columnIndex] {
-			get { return this[columnIndex, DataRowVersion.Default]; }
-			set {
-				if (columnIndex < 0 || columnIndex > _table.Columns.Count)
-					throw new IndexOutOfRangeException ();
-				if (rowState == DataRowState.Deleted)
-					throw new DeletedRowInaccessibleException ();
-				DataColumn column = _table.Columns[columnIndex];
-				_table.ChangingDataColumn (this, column, value);
-				
-				
-				bool orginalEditing = editing;
-				if (!orginalEditing) BeginEdit ();
-				object v = SetColumnValue (value, columnIndex);
-				proposed[columnIndex] = v;
-				_table.ChangedDataColumn (this, column, v);
-				if (!orginalEditing) EndEdit ();
-			}
-		}
-
-		/// <summary>
-		/// Gets the specified version of data stored in the named column.
-		/// </summary>
-		public object this[string columnName, DataRowVersion version] {
-			get {
-				int columnIndex = _table.Columns.IndexOf (columnName);
-				if (columnIndex == -1)
-					throw new IndexOutOfRangeException ();
-				return this[columnIndex, version];
-			}
-		}
-
-		/// <summary>
-		/// Gets the specified version of data stored in the specified DataColumn.
-		/// </summary>
-		public object this[DataColumn column, DataRowVersion version] {
-			get {
-				int columnIndex = _table.Columns.IndexOf (column);
-				if (columnIndex == -1)
-					throw new ArgumentException ("The column does not belong to this table.");
-				return this[columnIndex, version];
-			}
-		}
-
-		/// <summary>
-		/// Gets the data stored in the column, specified by index and version of the data to
-		/// retrieve.
-		/// </summary>
-		public object this[int columnIndex, DataRowVersion version] {
-			get {
-				if (columnIndex < 0 || columnIndex > _table.Columns.Count)
-					throw new IndexOutOfRangeException ();
-				// Non-existent version
-				if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
-					throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
-				// Accessing deleted rows
-				if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
-					throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
-				switch (version) {
-				case DataRowVersion.Default:
-					if (editing || rowState == DataRowState.Detached)
-						return proposed[columnIndex];
-					return current[columnIndex];
-				case DataRowVersion.Proposed:
-					return proposed[columnIndex];
-				case DataRowVersion.Current:
-					return current[columnIndex];
-				case DataRowVersion.Original:
-					return original[columnIndex];
-				default:
-					throw new ArgumentException ();
-				}
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets all of the values for this row through an array.
-		/// </summary>
-		[MonoTODO]
-		public object[] ItemArray {
-			get { 
-				return current; 
-			}
-			set {
-				if (value.Length > _table.Columns.Count)
-					throw new ArgumentException ();
-
-				if (rowState == DataRowState.Deleted)
-					throw new DeletedRowInaccessibleException ();
-				
-				object[] newItems = new object[_table.Columns.Count];			
-				object v = null;
-				for (int i = 0; i < _table.Columns.Count; i++) {
-
-					if (i < value.Length)
-						v = value[i];
-					else
-						v = null;
-
-					newItems[i] = SetColumnValue (v, i);
-				}
-
-				bool orginalEditing = editing;
-				if (!orginalEditing) BeginEdit ();
-				proposed = newItems;
-				if (!orginalEditing) EndEdit ();
-			}
-		}
-
-		private object SetColumnValue (object v, int index) 
-		{		
-			object newval = null;
-			DataColumn col = _table.Columns[index];
-			
-			if (col.ReadOnly && v != this[index])
-				throw new ReadOnlyException ();
-
-			if (v == null)
-			{
-				if(col.DefaultValue != DBNull.Value) 
-				{
-					newval = col.DefaultValue;
-				}
-				else if(col.AutoIncrement == true) 
-				{
-					newval = this [index];
-				}
-				else 
-				{
-					if (!col.AllowDBNull)
-					{
-						if (!this._table._duringDataLoad)
-						{
-							throw new NoNullAllowedException ();
-						}
-						else 
-						{
-							//Constraint violations during data load is raise in DataTable EndLoad
-							this._nullConstraintViolation = true;
-							
-						}
-					}
-
-					newval= DBNull.Value;
-					
-				
-				}
-			}		
-			else if (v == DBNull.Value) 
-			{
-				
-				if (!col.AllowDBNull)
-				{
-					if (!this._table._duringDataLoad)
-					{
-						throw new NoNullAllowedException ();
-					}
-					else 
-					{
-						//Constraint violations during data load is raise in DataTable EndLoad
-						this._nullConstraintViolation = true;
-							
-					}
-				}
-				newval= DBNull.Value;
-			}
-			else 
-			{	
-				Type vType = v.GetType(); // data type of value
-				Type cType = col.DataType; // column data type
-				if (cType != vType) 
-				{
-					TypeCode typeCode = Type.GetTypeCode(cType);
-					switch(typeCode) {
-					case TypeCode.Boolean :
-						v = Convert.ToBoolean (v);
-						break;
-					case TypeCode.Byte  :
-						v = Convert.ToByte (v);
-						break;
-					case TypeCode.Char  :
-						v = Convert.ToChar (v);
-						break;
-					case TypeCode.DateTime  :
-						v = Convert.ToDateTime (v);
-						break;
-					case TypeCode.Decimal  :
-						v = Convert.ToDecimal (v);
-						break;
-					case TypeCode.Double  :
-						v = Convert.ToDouble (v);
-						break;
-					case TypeCode.Int16  :
-						v = Convert.ToInt16 (v);
-						break;
-					case TypeCode.Int32  :
-						v = Convert.ToInt32 (v);
-						break;
-					case TypeCode.Int64  :
-						v = Convert.ToInt64 (v);
-						break;
-					case TypeCode.SByte  :
-						v = Convert.ToSByte (v);
-						break;
-					case TypeCode.Single  :
-						v = Convert.ToSingle (v);
-						break;
-					case TypeCode.String  :
-						v = Convert.ToString (v);
-						break;
-					case TypeCode.UInt16  :
-						v = Convert.ToUInt16 (v);
-						break;
-					case TypeCode.UInt32  :
-						v = Convert.ToUInt32 (v);
-						break;
-					case TypeCode.UInt64  :
-						v = Convert.ToUInt64 (v);
-						break;
-					default :
-					switch(cType.ToString()) {
-						case "System.TimeSpan" :
-							v = (System.TimeSpan) v;
-							break;
-						case "System.Type" :
-							v = (System.Type) v;
-							break;
-						case "System.Object" :
-							//v = (System.Object) v;
-							break;
-						default:
-							// FIXME: is exception correct?
-							throw new InvalidCastException("Type not supported.");
-					}
-						break;
-				}
-				vType = v.GetType();
-				}
-				newval = v;
-				if(col.AutoIncrement == true) {
-					long inc = Convert.ToInt64(v);
-					col.UpdateAutoIncrementValue (inc);
-				}
-			}
-			col.DataHasBeenSet = true;
-			return newval;
-		}
-
-		/// <summary>
-		/// Gets or sets the custom error description for a row.
-		/// </summary>
-		public string RowError {
-			get { return rowError; }
-			set { rowError = value; }
-		}
-
-		/// <summary>
-		/// Gets the current state of the row in regards to its relationship to the
-		/// DataRowCollection.
-		/// </summary>
-		public DataRowState RowState {
-			get { return rowState; }
-		}
-
-		//FIXME?: Couldn't find a way to set the RowState when adding the DataRow
-		//to a Datatable so I added this method. Delete if there is a better way.
-		internal void AttachRow() {
-			current = proposed;
-			proposed = null;
-			rowState = DataRowState.Added;
-		}
-
-		//FIXME?: Couldn't find a way to set the RowState when removing the DataRow
-		//from a Datatable so I added this method. Delete if there is a better way.
-		internal void DetachRow() {
-			proposed = null;
-			rowState = DataRowState.Detached;
-		}
-
-		/// <summary>
-		/// Gets the DataTable for which this row has a schema.
-		/// </summary>
-		public DataTable Table {
-			get { return _table; }
-		}
-
-		/// <summary>
-		/// Gets and sets index of row. This is used from 
-		/// XmlDataDocument.
-		// </summary>
-		internal int XmlRowID {
-			get { return xmlRowID; }
-			set { xmlRowID = value; }
-		}
-
-		#endregion
-
-		#region Methods
-
-		/// <summary>
-		/// Commits all the changes made to this row since the last time AcceptChanges was
-		/// called.
-		/// </summary>
-		public void AcceptChanges () 
-		{
-			EndEdit(); // in case it hasn't been called
-			switch (rowState) {
-			case DataRowState.Added:
-			case DataRowState.Modified:
-				rowState = DataRowState.Unchanged;
-				break;
-			case DataRowState.Deleted:
-				_table.Rows.Remove (this);
-				break;
-			case DataRowState.Detached:
-				throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
-			}
-			// Accept from detached
-			if (original == null)
-				original = new object[_table.Columns.Count];
-			Array.Copy (current, original, _table.Columns.Count);
-		}
-
-		/// <summary>
-		/// Begins an edit operation on a DataRow object.
-		/// </summary>
-		[MonoTODO]
-		public void BeginEdit () 
-		{
-			if (rowState == DataRowState.Deleted)
-				throw new DeletedRowInaccessibleException ();
-			if (!HasVersion (DataRowVersion.Proposed)) {
-				proposed = new object[_table.Columns.Count];
-				Array.Copy (current, proposed, current.Length);
-			}
-			//TODO: Suspend validation
-			editing = true;
-		}
-
-		/// <summary>
-		/// Cancels the current edit on the row.
-		/// </summary>
-		[MonoTODO]
-		public void CancelEdit () 
-		{
-			editing = false;
-			//TODO: Events
-			if (HasVersion (DataRowVersion.Proposed)) {
-				proposed = null;
-				if (rowState == DataRowState.Modified)
-				    rowState = DataRowState.Unchanged;
-			}
-		}
-
-		/// <summary>
-		/// Clears the errors for the row, including the RowError and errors set with
-		/// SetColumnError.
-		/// </summary>
-		public void ClearErrors () 
-		{
-			rowError = String.Empty;
-			columnErrors = new String[_table.Columns.Count];
-		}
-
-		/// <summary>
-		/// Deletes the DataRow.
-		/// </summary>
-		[MonoTODO]
-		public void Delete () 
-		{
-			_table.DeletingDataRow(this, DataRowAction.Delete);
-			switch (rowState) {
-			case DataRowState.Added:
-				Table.Rows.Remove (this);
-				break;
-			case DataRowState.Deleted:
-				throw new DeletedRowInaccessibleException ();
-			default:
-				// check what to do with child rows
-				CheckChildRows(DataRowAction.Delete);
-				rowState = DataRowState.Deleted;
-				break;
-			}
-			_table.DeletedDataRow(this, DataRowAction.Delete);
-		}
-
-		// check the child rows of this row before deleting the row.
-		private void CheckChildRows(DataRowAction action)
-		{
-			
-			// in this method we find the row that this row is in a reltion with them.
-			// in shortly we find all child rows of this row.
-			// then we function according to the DeleteRule of the foriegnkey.
-
-
-			// 1. find if this row is attached to dataset.
-			// 2. find if EnforceConstraints is true.
-			// 3. find if there are any constraint on the table that the row is in.
-			if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
-			{
-				// loop on all relations of the dataset.
-				DataRelationCollection relCollection = _table.DataSet.Relations;
-				for (int i = 0; i < relCollection.Count; i++)
-				{
-					DataRelation rel = relCollection[i];
-					// we want only relations that their parent table is the table this row is in.
-					// that is because we interesting only in relations that the row is a parent of
-					// other rows.
-					if (rel.ParentTable == _table)
-					{
-						Rule rule;
-						if (action == DataRowAction.Delete)
-							rule = rel.ChildKeyConstraint.DeleteRule;
-						else
-							rule = rel.ChildKeyConstraint.UpdateRule;
-						
-						DataRow[] childRows = GetChildRows(rel);
-						switch (rule)
-						{
-							case Rule.Cascade:  // delete or change all relted rows.
-								if (childRows != null)
-								{
-									for (int j = 0; j < childRows.Length; j++)
-									{
-										// if action is delete we delte all child rows
-										if (action == DataRowAction.Delete)
-											childRows[j].Delete();
-										// if action is change we change the values in the child row
-										else if (action == DataRowAction.Change)
-										{
-											// change only the values in the key columns
-											// set the childcolumn value to the new parent row value
-											for (int k = 0; k < rel.ChildColumns.Length; k++)
-												childRows[j][rel.ChildColumns[k]] = this[rel.ParentColumns[k], DataRowVersion.Proposed];
-										}
-									}
-								}
-								break;
-							case Rule.None: // throw an exception if there are any child rows.
-								if (childRows != null)
-								{
-									string changeStr = "Cannot change this row because constraints are enforced on relation " + rel.RelationName +", and changing this row will strand child rows.";
-									string delStr = "Cannot delete this row because constraints are enforced on relation " + rel.RelationName +", and deleting this row will strand child rows.";
-									string message = action == DataRowAction.Delete ? delStr : changeStr;
-									throw new InvalidConstraintException(message);
-								}
-								break;
-							case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
-								if (childRows != null)
-								{
-									for (int j = 0; j < childRows.Length; j++)
-									{
-										DataRow child = childRows[j];
-										//set only the key columns to default
-										for (int k = 0; k < rel.ChildColumns.Length; k++)
-											child[rel.ChildColumns[k]] = rel.ChildColumns[k].DefaultValue;
-									}
-								}
-								break;
-							case Rule.SetNull: // set the values in the child row to null.
-								if (childRows != null)
-								{
-									for (int j = 0; j < childRows.Length; j++)
-									{
-										DataRow child = childRows[j];
-										// set only the key columns to DBNull
-										for (int k = 0; k < rel.ChildColumns.Length; k++)
-											child.SetNull(rel.ChildColumns[k]);
-									}
-								}
-								break;
-						}
-							
-					}
-				}
-			}
-		}
-
-		/// <summary>
-		/// Ends the edit occurring on the row.
-		/// </summary>
-		[MonoTODO]
-		public void EndEdit () 
-		{
-			editing = false;
-			if (rowState == DataRowState.Detached)
-				return;
-			if (HasVersion (DataRowVersion.Proposed))
-			{
-				_table.ChangingDataRow(this, DataRowAction.Change);
-				if (rowState == DataRowState.Unchanged)
-					rowState = DataRowState.Modified;
-				
-				//Calling next method validates UniqueConstraints
-				//and ForeignKeys.
-				try
-				{
-					if (_table.DataSet == null || _table.DataSet.EnforceConstraints)
-						_table.Rows.ValidateDataRowInternal(this);
-				}
-				catch (Exception e)
-				{
-					proposed = null;
-					throw e;
-				}
-				// check all child rows.
-				CheckChildRows(DataRowAction.Change);
-				current = proposed;
-				proposed = null;
-				_table.ChangedDataRow(this, DataRowAction.Change);
-			}
-		}
-
-		/// <summary>
-		/// Gets the child rows of this DataRow using the specified DataRelation.
-		/// </summary>
-		public DataRow[] GetChildRows (DataRelation relation) 
-		{
-			return GetChildRows (relation, DataRowVersion.Current);
-		}
-
-		/// <summary>
-		/// Gets the child rows of a DataRow using the specified RelationName of a
-		/// DataRelation.
-		/// </summary>
-		public DataRow[] GetChildRows (string relationName) 
-		{
-			return GetChildRows (Table.DataSet.Relations[relationName]);
-		}
-
-		/// <summary>
-		/// Gets the child rows of a DataRow using the specified DataRelation, and
-		/// DataRowVersion.
-		/// </summary>
-		public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
-		{
-			// TODO: Caching for better preformance
-			ArrayList rows = new ArrayList();
-			DataColumn[] parentColumns = relation.ParentColumns;
-			DataColumn[] childColumns = relation.ChildColumns;
-			int numColumn = parentColumns.Length;
-			if (HasVersion(version)) 
-			{
-				foreach (DataRow row in relation.ChildTable.Rows) 
-				{
-					bool allColumnsMatch = false;
-					if (row.HasVersion(DataRowVersion.Default))
-					{
-						allColumnsMatch = true;
-						for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
-						{
-							if (!this[parentColumns[columnCnt], version].Equals(
-								row[childColumns[columnCnt], DataRowVersion.Default])) 
-							{
-								allColumnsMatch = false;
-								break;
-							}
-						}
-					}
-					if (allColumnsMatch) rows.Add(row);
-				}
-			}
-			return rows.ToArray(typeof(DataRow)) as DataRow[];
-		}
-
-		/// <summary>
-		/// Gets the child rows of a DataRow using the specified RelationName of a
-		/// DataRelation, and DataRowVersion.
-		/// </summary>
-		public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
-		{
-			return GetChildRows (Table.DataSet.Relations[relationName], version);
-		}
-
-		/// <summary>
-		/// Gets the error description of the specified DataColumn.
-		/// </summary>
-		public string GetColumnError (DataColumn column) 
-		{
-			return GetColumnError (_table.Columns.IndexOf(column));
-		}
-
-		/// <summary>
-		/// Gets the error description for the column specified by index.
-		/// </summary>
-		public string GetColumnError (int columnIndex) 
-		{
-			if (columnIndex < 0 || columnIndex >= columnErrors.Length)
-				throw new IndexOutOfRangeException ();
-
-			string retVal = columnErrors[columnIndex];
-			if (retVal == null)
-				retVal = string.Empty;
-			return retVal;
-		}
-
-		/// <summary>
-		/// Gets the error description for the column, specified by name.
-		/// </summary>
-		public string GetColumnError (string columnName) 
-		{
-			return GetColumnError (_table.Columns.IndexOf(columnName));
-		}
-
-		/// <summary>
-		/// Gets an array of columns that have errors.
-		/// </summary>
-		public DataColumn[] GetColumnsInError () 
-		{
-			ArrayList dataColumns = new ArrayList ();
-
-			for (int i = 0; i < columnErrors.Length; i += 1)
-			{
-				if (columnErrors[i] != null && columnErrors[i] != String.Empty)
-					dataColumns.Add (_table.Columns[i]);
-			}
-
-			return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
-		}
-
-		/// <summary>
-		/// Gets the parent row of a DataRow using the specified DataRelation.
-		/// </summary>
-		public DataRow GetParentRow (DataRelation relation) 
-		{
-			return GetParentRow (relation, DataRowVersion.Current);
-		}
-
-		/// <summary>
-		/// Gets the parent row of a DataRow using the specified RelationName of a
-		/// DataRelation.
-		/// </summary>
-		public DataRow GetParentRow (string relationName) 
-		{
-			return GetParentRow (relationName, DataRowVersion.Current);
-		}
-
-		/// <summary>
-		/// Gets the parent row of a DataRow using the specified DataRelation, and
-		/// DataRowVersion.
-		/// </summary>
-		public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
-		{
-			DataRow[] rows = GetParentRows(relation, version);
-			if (rows.Length == 0) return null;
-			return rows[0];
-		}
-
-		/// <summary>
-		/// Gets the parent row of a DataRow using the specified RelationName of a 
-		/// DataRelation, and DataRowVersion.
-		/// </summary>
-		public DataRow GetParentRow (string relationName, DataRowVersion version) 
-		{
-			return GetParentRow (Table.DataSet.Relations[relationName], version);
-		}
-
-		/// <summary>
-		/// Gets the parent rows of a DataRow using the specified DataRelation.
-		/// </summary>
-		public DataRow[] GetParentRows (DataRelation relation) 
-		{
-			return GetParentRows (relation, DataRowVersion.Current);
-		}
-
-		/// <summary>
-		/// Gets the parent rows of a DataRow using the specified RelationName of a 
-		/// DataRelation.
-		/// </summary>
-		public DataRow[] GetParentRows (string relationName) 
-		{
-			return GetParentRows (relationName, DataRowVersion.Current);
-		}
-
-		/// <summary>
-		/// Gets the parent rows of a DataRow using the specified DataRelation, and
-		/// DataRowVersion.
-		/// </summary>
-		public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
-		{
-			// TODO: Caching for better preformance
-			ArrayList rows = new ArrayList();
-			DataColumn[] parentColumns = relation.ParentColumns;
-			DataColumn[] childColumns = relation.ChildColumns;
-			int numColumn = parentColumns.Length;
-			if (HasVersion(version))
-			{
-				foreach (DataRow row in relation.ParentTable.Rows) 
-				{
-					bool allColumnsMatch = false;
-					if (row.HasVersion(DataRowVersion.Default))
-					{
-						allColumnsMatch = true;
-						for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
-						{
-							if (!this[parentColumns[columnCnt], version].Equals(
-								row[childColumns[columnCnt], DataRowVersion.Default])) 
-							{
-								allColumnsMatch = false;
-								break;
-							}
-						}
-					}
-					if (allColumnsMatch) rows.Add(row);
-				}
-			}
-			return rows.ToArray(typeof(DataRow)) as DataRow[];
-		}
-
-		/// <summary>
-		/// Gets the parent rows of a DataRow using the specified RelationName of a 
-		/// DataRelation, and DataRowVersion.
-		/// </summary>
-		public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
-		{
-			return GetParentRows (Table.DataSet.Relations[relationName], version);
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether a specified version exists.
-		/// </summary>
-		public bool HasVersion (DataRowVersion version) 
-		{
-			switch (version)
-			{
-				case DataRowVersion.Default:
-					if (rowState == DataRowState.Deleted)
-						return false;
-					if (rowState == DataRowState.Detached)
-						return proposed != null;
-					return true;
-				case DataRowVersion.Proposed:
-					if (rowState == DataRowState.Deleted)
-						return false;
-					return (proposed != null);
-				case DataRowVersion.Current:
-					if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
-						return false;
-					return (current != null);
-				case DataRowVersion.Original:
-					if (rowState == DataRowState.Detached)
-						return false;
-					return (original != null);
-			}
-			return false;
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether the specified DataColumn contains a null value.
-		/// </summary>
-		public bool IsNull (DataColumn column) 
-		{
-			object o = this[column];
-			return (o == null || o == DBNull.Value);
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether the column at the specified index contains a null
-		/// value.
-		/// </summary>
-		public bool IsNull (int columnIndex) 
-		{
-			object o = this[columnIndex];
-			return (o == null || o == DBNull.Value);
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether the named column contains a null value.
-		/// </summary>
-		public bool IsNull (string columnName) 
-		{
-			object o = this[columnName];
-			return (o == null || o == DBNull.Value);
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether the specified DataColumn and DataRowVersion
-		/// contains a null value.
-		/// </summary>
-		public bool IsNull (DataColumn column, DataRowVersion version) 
-		{
-			object o = this[column, version];
-			return (o == null || o == DBNull.Value);
-		}
-
-		/// <summary>
-		/// Rejects all changes made to the row since AcceptChanges was last called.
-		/// </summary>
-		public void RejectChanges () 
-		{
-			// If original is null, then nothing has happened since AcceptChanges
-			// was last called.  We have no "original" to go back to.
-			if (original != null)
-			{
-				Array.Copy (original, current, _table.Columns.Count);
-			       
-				_table.ChangedDataRow (this, DataRowAction.Rollback);
-				CancelEdit ();
-				switch (rowState)
-				{
-					case DataRowState.Added:
-						_table.Rows.Remove (this);
-						break;
-					case DataRowState.Modified:
-						rowState = DataRowState.Unchanged;
-						break;
-					case DataRowState.Deleted:
-						rowState = DataRowState.Unchanged;
-						break;
-				} 
-				
-			} 			
-			else {
-				// If rows are just loaded via Xml the original values are null.
-				// So in this case we have to remove all columns.
-				// FIXME: I'm not realy sure, does this break something else, but
-				// if so: FIXME ;)
-				
-				if ((rowState & DataRowState.Added) > 0)
-					_table.Rows.Remove (this);
-			}
-		}
-
-		/// <summary>
-		/// Sets the error description for a column specified as a DataColumn.
-		/// </summary>
-		public void SetColumnError (DataColumn column, string error) 
-		{
-			SetColumnError (_table.Columns.IndexOf (column), error);
-		}
-
-		/// <summary>
-		/// Sets the error description for a column specified by index.
-		/// </summary>
-		public void SetColumnError (int columnIndex, string error) 
-		{
-			if (columnIndex < 0 || columnIndex >= columnErrors.Length)
-				throw new IndexOutOfRangeException ();
-			columnErrors[columnIndex] = error;
-		}
-
-		/// <summary>
-		/// Sets the error description for a column specified by name.
-		/// </summary>
-		public void SetColumnError (string columnName, string error) 
-		{
-			SetColumnError (_table.Columns.IndexOf (columnName), error);
-		}
-
-		/// <summary>
-		/// Sets the value of the specified DataColumn to a null value.
-		/// </summary>
-		protected void SetNull (DataColumn column) 
-		{
-			this[column] = DBNull.Value;
-		}
-
-		/// <summary>
-		/// Sets the parent row of a DataRow with specified new parent DataRow.
-		/// </summary>
-		[MonoTODO]
-		public void SetParentRow (DataRow parentRow) 
-		{
-			throw new NotImplementedException ();
-		}
-
-		/// <summary>
-		/// Sets the parent row of a DataRow with specified new parent DataRow and
-		/// DataRelation.
-		/// </summary>
-		[MonoTODO]
-		public void SetParentRow (DataRow parentRow, DataRelation relation) 
-		{
-			throw new NotImplementedException ();
-		}
-		
-		//Copy all values of this DataaRow to the row parameter.
-		internal void CopyValuesToRow(DataRow row)
-		{
-						
-			if (row == null)
-				throw new ArgumentNullException("row");
-			if (row == this)
-				throw new ArgumentException("'row' is the same as this object");
-
-			DataColumnCollection columns = Table.Columns;
-			
-			for(int i = 0; i < columns.Count; i++){
-
-				string columnName = columns[i].ColumnName;
-				int index = row.Table.Columns.IndexOf(columnName);
-				//if a column with the same name exists in both rows copy the values
-				if(index != -1) {
-					if (HasVersion(DataRowVersion.Original))
-					{
-						if (row.original == null)
-							row.original = new object[row.Table.Columns.Count];
-						row.original[index] = row.SetColumnValue(original[i], index);
-					}
-					if (HasVersion(DataRowVersion.Current))
-					{
-						if (row.current == null)
-							row.current = new object[row.Table.Columns.Count];
-						row.current[index] = row.SetColumnValue(current[i], index);
-					}
-					if (HasVersion(DataRowVersion.Proposed))
-					{
-						if (row.proposed == null)
-							row.proposed = new object[row.Table.Columns.Count];
-						row.proposed[index] = row.SetColumnValue(proposed[i], index);
-					}
-					
-					//Saving the current value as the column value
-					row[index] = row.current[index];
-					
-				}
-			}
-
-			row.rowState = RowState;
-			row.RowError = RowError;
-			row.columnErrors = columnErrors;
-		}
-
-		
-		public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
-		{
-			// if a column is added we hava to add an additional value the 
-			// the priginal, current and propoed arrays.
-			// this scenario can happened in merge operation.
-
-			if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
-			{
-				object[] tmp;
-				if (current != null)
-				{
-					tmp = new object[current.Length + 1];
-					Array.Copy (current, tmp, current.Length);
-					tmp[tmp.Length - 1] = DBNull.Value;
-					current = tmp;
-				}
-				if (proposed != null)
-				{
-					tmp = new object[proposed.Length + 1];
-					Array.Copy (proposed, tmp, proposed.Length);
-					tmp[tmp.Length - 1] = DBNull.Value;
-					proposed = tmp;
-				}
-				if(original != null)
-				{
-					tmp = new object[original.Length + 1];
-					Array.Copy (original, tmp, original.Length);
-					tmp[tmp.Length - 1] = DBNull.Value;
-					original = tmp;
-				}
-
-			}
-		}
-
-		internal bool IsRowChanged(DataRowState rowState) {
-			if((RowState & rowState) != 0)
-				return true;
-
-			//we need to find if child rows of this row changed.
-			//if yes - we should return true
-
-			// if the rowState is deleted we should get the original version of the row
-			// else - we should get the current version of the row.
-			DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
-			int count = Table.ChildRelations.Count;
-			for (int i = 0; i < count; i++){
-				DataRelation rel = Table.ChildRelations[i];
-				DataRow[] childRows = GetChildRows(rel, version);
-				for (int j = 0; j < childRows.Length; j++){
-					if (childRows[j].IsRowChanged(rowState))
-						return true;
-				}
-			}
-
-			return false;
-		}
-
-		#endregion // Methods
-	}
-
-	
-
-}
+//
+// System.Data.DataRow.cs
+//
+// Author:
+//   Rodrigo Moya <[email protected]>
+//   Daniel Morgan <[email protected]>
+//   Tim Coleman <[email protected]>
+//   Ville Palo <[email protected]>
+//   Alan Tam Siu Lung <[email protected]>
+//
+// (C) Ximian, Inc 2002
+// (C) Daniel Morgan 2002, 2003
+// Copyright (C) 2002 Tim Coleman
+//
+
+using System;
+using System.Collections;
+using System.Globalization;
+
+namespace System.Data {
+	/// <summary>
+	/// Represents a row of data in a DataTable.
+	/// </summary>
+	[Serializable]
+	public class DataRow
+	{
+		#region Fields
+
+		private DataTable _table;
+
+		private object[] original;
+		private object[] proposed;
+		private object[] current;
+
+		private string[] columnErrors;
+		private string rowError;
+		private DataRowState rowState;
+		internal int xmlRowID = 0;
+		internal bool _nullConstraintViolation;
+		private bool editing = false;
+		private bool _hasParentCollection;
+
+		#endregion
+
+		#region Constructors
+
+		/// <summary>
+		/// This member supports the .NET Framework infrastructure and is not intended to be 
+		/// used directly from your code.
+		/// </summary>
+		protected internal DataRow (DataRowBuilder builder)
+		{
+			_table = builder.Table;
+
+			original = null; 
+			
+			proposed = new object[_table.Columns.Count];
+			for (int c = 0; c < _table.Columns.Count; c++) 
+			{
+				proposed[c] = DBNull.Value;
+			}
+			
+			columnErrors = new string[_table.Columns.Count];
+			rowError = String.Empty;
+
+			//on first creating a DataRow it is always detached.
+			rowState = DataRowState.Detached;
+			
+			foreach (DataColumn Col in _table.Columns) {
+				
+				if (Col.AutoIncrement) {
+					this [Col] = Col.AutoIncrementValue();
+				}
+			}
+			_table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
+		}
+
+		
+		#endregion
+
+		#region Properties
+
+		/// <summary>
+		/// Gets a value indicating whether there are errors in a row.
+		/// </summary>
+		public bool HasErrors {
+			[MonoTODO]
+			get {
+				if (RowError != string.Empty)
+					return true;
+
+				for (int i= 0; i < columnErrors.Length; i++){
+					if (columnErrors[i] != null && columnErrors[i] != string.Empty)
+						return true;
+				}
+
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the data stored in the column specified by name.
+		/// </summary>
+		public object this[string columnName] {
+			get { return this[columnName, DataRowVersion.Default]; }
+			set {
+				int columnIndex = _table.Columns.IndexOf (columnName);
+				if (columnIndex == -1)
+					throw new IndexOutOfRangeException ();
+				this[columnIndex] = value;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the data stored in specified DataColumn
+		/// </summary>
+		public object this[DataColumn column] {
+
+			get {
+				return this[column, DataRowVersion.Default];} 
+			set {
+				int columnIndex = _table.Columns.IndexOf (column);
+				if (columnIndex == -1)
+					throw new ArgumentException ("The column does not belong to this table.");
+				this[columnIndex] = value;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the data stored in column specified by index.
+		/// </summary>
+		public object this[int columnIndex] {
+			get { return this[columnIndex, DataRowVersion.Default]; }
+			set {
+				if (columnIndex < 0 || columnIndex > _table.Columns.Count)
+					throw new IndexOutOfRangeException ();
+				if (rowState == DataRowState.Deleted)
+					throw new DeletedRowInaccessibleException ();
+				DataColumn column = _table.Columns[columnIndex];
+				_table.ChangingDataColumn (this, column, value);
+				
+				
+				bool orginalEditing = editing;
+				if (!orginalEditing) BeginEdit ();
+				object v = SetColumnValue (value, columnIndex);
+				proposed[columnIndex] = v;
+				_table.ChangedDataColumn (this, column, v);
+				if (!orginalEditing) EndEdit ();
+			}
+		}
+
+		/// <summary>
+		/// Gets the specified version of data stored in the named column.
+		/// </summary>
+		public object this[string columnName, DataRowVersion version] {
+			get {
+				int columnIndex = _table.Columns.IndexOf (columnName);
+				if (columnIndex == -1)
+					throw new IndexOutOfRangeException ();
+				return this[columnIndex, version];
+			}
+		}
+
+		/// <summary>
+		/// Gets the specified version of data stored in the specified DataColumn.
+		/// </summary>
+		public object this[DataColumn column, DataRowVersion version] {
+			get {
+				int columnIndex = _table.Columns.IndexOf (column);
+				if (columnIndex == -1)
+					throw new ArgumentException ("The column does not belong to this table.");
+				return this[columnIndex, version];
+			}
+		}
+
+		/// <summary>
+		/// Gets the data stored in the column, specified by index and version of the data to
+		/// retrieve.
+		/// </summary>
+		public object this[int columnIndex, DataRowVersion version] {
+			get {
+				if (columnIndex < 0 || columnIndex > _table.Columns.Count)
+					throw new IndexOutOfRangeException ();
+				// Non-existent version
+				if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
+					throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
+				// Accessing deleted rows
+				if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
+					throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
+				switch (version) {
+				case DataRowVersion.Default:
+					if (editing || rowState == DataRowState.Detached)
+						return proposed[columnIndex];
+					return current[columnIndex];
+				case DataRowVersion.Proposed:
+					return proposed[columnIndex];
+				case DataRowVersion.Current:
+					return current[columnIndex];
+				case DataRowVersion.Original:
+					return original[columnIndex];
+				default:
+					throw new ArgumentException ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets all of the values for this row through an array.
+		/// </summary>
+		[MonoTODO]
+		public object[] ItemArray {
+			get { 
+				return current; 
+			}
+			set {
+				if (value.Length > _table.Columns.Count)
+					throw new ArgumentException ();
+
+				if (rowState == DataRowState.Deleted)
+					throw new DeletedRowInaccessibleException ();
+				
+				object[] newItems = new object[_table.Columns.Count];			
+				object v = null;
+				for (int i = 0; i < _table.Columns.Count; i++) {
+
+					if (i < value.Length)
+						v = value[i];
+					else
+						v = null;
+
+					newItems[i] = SetColumnValue (v, i);
+				}
+
+				bool orginalEditing = editing;
+				if (!orginalEditing) BeginEdit ();
+				proposed = newItems;
+				if (!orginalEditing) EndEdit ();
+			}
+		}
+
+		private object SetColumnValue (object v, int index) 
+		{		
+			object newval = null;
+			DataColumn col = _table.Columns[index];
+			
+			if (_hasParentCollection && col.ReadOnly && v != this[index])
+				throw new ReadOnlyException ();
+
+			if (v == null)
+			{
+				if(col.DefaultValue != DBNull.Value) 
+				{
+					newval = col.DefaultValue;
+				}
+				else if(col.AutoIncrement == true) 
+				{
+					newval = this [index];
+				}
+				else 
+				{
+					if (!col.AllowDBNull)
+					{
+						if (!this._table._duringDataLoad)
+						{
+							throw new NoNullAllowedException ();
+						}
+						else 
+						{
+							//Constraint violations during data load is raise in DataTable EndLoad
+							this._nullConstraintViolation = true;
+							
+						}
+					}
+
+					newval= DBNull.Value;
+					
+				
+				}
+			}		
+			else if (v == DBNull.Value) 
+			{
+				
+				if (!col.AllowDBNull)
+				{
+					if (!this._table._duringDataLoad)
+					{
+						throw new NoNullAllowedException ();
+					}
+					else 
+					{
+						//Constraint violations during data load is raise in DataTable EndLoad
+						this._nullConstraintViolation = true;
+							
+					}
+				}
+				newval= DBNull.Value;
+			}
+			else 
+			{	
+				Type vType = v.GetType(); // data type of value
+				Type cType = col.DataType; // column data type
+				if (cType != vType) 
+				{
+					TypeCode typeCode = Type.GetTypeCode(cType);
+					switch(typeCode) {
+					case TypeCode.Boolean :
+						v = Convert.ToBoolean (v);
+						break;
+					case TypeCode.Byte  :
+						v = Convert.ToByte (v);
+						break;
+					case TypeCode.Char  :
+						v = Convert.ToChar (v);
+						break;
+					case TypeCode.DateTime  :
+						v = Convert.ToDateTime (v);
+						break;
+					case TypeCode.Decimal  :
+						v = Convert.ToDecimal (v);
+						break;
+					case TypeCode.Double  :
+						v = Convert.ToDouble (v);
+						break;
+					case TypeCode.Int16  :
+						v = Convert.ToInt16 (v);
+						break;
+					case TypeCode.Int32  :
+						v = Convert.ToInt32 (v);
+						break;
+					case TypeCode.Int64  :
+						v = Convert.ToInt64 (v);
+						break;
+					case TypeCode.SByte  :
+						v = Convert.ToSByte (v);
+						break;
+					case TypeCode.Single  :
+						v = Convert.ToSingle (v);
+						break;
+					case TypeCode.String  :
+						v = Convert.ToString (v);
+						break;
+					case TypeCode.UInt16  :
+						v = Convert.ToUInt16 (v);
+						break;
+					case TypeCode.UInt32  :
+						v = Convert.ToUInt32 (v);
+						break;
+					case TypeCode.UInt64  :
+						v = Convert.ToUInt64 (v);
+						break;
+					default :
+					switch(cType.ToString()) {
+						case "System.TimeSpan" :
+							v = (System.TimeSpan) v;
+							break;
+						case "System.Type" :
+							v = (System.Type) v;
+							break;
+						case "System.Object" :
+							//v = (System.Object) v;
+							break;
+						default:
+							// FIXME: is exception correct?
+							throw new InvalidCastException("Type not supported.");
+					}
+						break;
+				}
+				vType = v.GetType();
+				}
+				newval = v;
+				if(col.AutoIncrement == true) {
+					long inc = Convert.ToInt64(v);
+					col.UpdateAutoIncrementValue (inc);
+				}
+			}
+			col.DataHasBeenSet = true;
+			return newval;
+		}
+
+		/// <summary>
+		/// Gets or sets the custom error description for a row.
+		/// </summary>
+		public string RowError {
+			get { return rowError; }
+			set { rowError = value; }
+		}
+
+		/// <summary>
+		/// Gets the current state of the row in regards to its relationship to the
+		/// DataRowCollection.
+		/// </summary>
+		public DataRowState RowState {
+			get { return rowState; }
+		}
+
+		//FIXME?: Couldn't find a way to set the RowState when adding the DataRow
+		//to a Datatable so I added this method. Delete if there is a better way.
+		internal void AttachRow() {
+			current = proposed;
+			proposed = null;
+			rowState = DataRowState.Added;
+		}
+
+		//FIXME?: Couldn't find a way to set the RowState when removing the DataRow
+		//from a Datatable so I added this method. Delete if there is a better way.
+		internal void DetachRow() {
+			proposed = null;
+			_hasParentCollection = false;
+			rowState = DataRowState.Detached;
+		}
+
+		/// <summary>
+		/// Gets the DataTable for which this row has a schema.
+		/// </summary>
+		public DataTable Table {
+			get { return _table; }
+		}
+
+		/// <summary>
+		/// Gets and sets index of row. This is used from 
+		/// XmlDataDocument.
+		// </summary>
+		internal int XmlRowID {
+			get { return xmlRowID; }
+			set { xmlRowID = value; }
+		}
+
+		#endregion
+
+		#region Methods
+
+		/// <summary>
+		/// Commits all the changes made to this row since the last time AcceptChanges was
+		/// called.
+		/// </summary>
+		public void AcceptChanges () 
+		{
+			EndEdit(); // in case it hasn't been called
+			switch (rowState) {
+			case DataRowState.Added:
+			case DataRowState.Modified:
+				rowState = DataRowState.Unchanged;
+				break;
+			case DataRowState.Deleted:
+				_table.Rows.Remove (this);
+				break;
+			case DataRowState.Detached:
+				throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
+			}
+			// Accept from detached
+			if (original == null)
+				original = new object[_table.Columns.Count];
+			Array.Copy (current, original, _table.Columns.Count);
+		}
+
+		/// <summary>
+		/// Begins an edit operation on a DataRow object.
+		/// </summary>
+		[MonoTODO]
+		public void BeginEdit () 
+		{
+			if (rowState == DataRowState.Deleted)
+				throw new DeletedRowInaccessibleException ();
+			if (!HasVersion (DataRowVersion.Proposed)) {
+				proposed = new object[_table.Columns.Count];
+				Array.Copy (current, proposed, current.Length);
+			}
+			//TODO: Suspend validation
+			editing = true;
+		}
+
+		/// <summary>
+		/// Cancels the current edit on the row.
+		/// </summary>
+		[MonoTODO]
+		public void CancelEdit () 
+		{
+			editing = false;
+			//TODO: Events
+			if (HasVersion (DataRowVersion.Proposed)) {
+				proposed = null;
+				if (rowState == DataRowState.Modified)
+				    rowState = DataRowState.Unchanged;
+			}
+		}
+
+		/// <summary>
+		/// Clears the errors for the row, including the RowError and errors set with
+		/// SetColumnError.
+		/// </summary>
+		public void ClearErrors () 
+		{
+			rowError = String.Empty;
+			columnErrors = new String[_table.Columns.Count];
+		}
+
+		/// <summary>
+		/// Deletes the DataRow.
+		/// </summary>
+		[MonoTODO]
+		public void Delete () 
+		{
+			_table.DeletingDataRow(this, DataRowAction.Delete);
+			switch (rowState) {
+			case DataRowState.Added:
+				Table.Rows.Remove (this);
+				break;
+			case DataRowState.Deleted:
+				throw new DeletedRowInaccessibleException ();
+			default:
+				// check what to do with child rows
+				CheckChildRows(DataRowAction.Delete);
+				rowState = DataRowState.Deleted;
+				break;
+			}
+			_table.DeletedDataRow(this, DataRowAction.Delete);
+		}
+
+		// check the child rows of this row before deleting the row.
+		private void CheckChildRows(DataRowAction action)
+		{
+			
+			// in this method we find the row that this row is in a reltion with them.
+			// in shortly we find all child rows of this row.
+			// then we function according to the DeleteRule of the foriegnkey.
+
+
+			// 1. find if this row is attached to dataset.
+			// 2. find if EnforceConstraints is true.
+			// 3. find if there are any constraint on the table that the row is in.
+			if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
+			{
+				foreach (DataTable table in _table.DataSet.Tables)
+				{
+					// loop on all constraints of the table.
+					ConstraintCollection constraintsCollection = table.Constraints;
+					for (int i = 0; i < constraintsCollection.Count; i++)
+					{
+						ForeignKeyConstraint fk = null;
+						if (constraintsCollection[i] is ForeignKeyConstraint)
+						{
+							fk = (ForeignKeyConstraint)constraintsCollection[i];
+							if (fk.RelatedTable == _table)
+							{
+								//we create a dummy relation because we do not want to duplicate code of GetChild().
+								// we use the dummy relation to find child rows.
+								DataRelation rel = new DataRelation("dummy", fk.RelatedColumns, fk.Columns, false);
+								Rule rule;
+								if (action == DataRowAction.Delete)
+									rule = fk.DeleteRule;
+								else
+									rule = fk.UpdateRule;
+								CheckChildRows(rel, action, rule);
+							}
+						}			
+					}
+				}
+			}
+		}
+
+		private void CheckChildRows(DataRelation rel, DataRowAction action, Rule rule)
+		{				
+			DataRow[] childRows = GetChildRows(rel);
+			switch (rule)
+			{
+				case Rule.Cascade:  // delete or change all relted rows.
+					if (childRows != null)
+					{
+						for (int j = 0; j < childRows.Length; j++)
+						{
+							// if action is delete we delete all child rows
+							if (action == DataRowAction.Delete)
+							{
+								if (childRows[j].RowState != DataRowState.Deleted)
+									childRows[j].Delete();
+							}
+							// if action is change we change the values in the child row
+							else if (action == DataRowAction.Change)
+							{
+								// change only the values in the key columns
+								// set the childcolumn value to the new parent row value
+								for (int k = 0; k < rel.ChildColumns.Length; k++)
+									childRows[j][rel.ChildColumns[k]] = this[rel.ParentColumns[k], DataRowVersion.Proposed];
+							}
+						}
+					}
+					break;
+				case Rule.None: // throw an exception if there are any child rows.
+					if (childRows != null)
+					{
+						for (int j = 0; j < childRows.Length; j++)
+						{
+							if (childRows[j].RowState != DataRowState.Deleted)
+							{
+								string changeStr = "Cannot change this row because constraints are enforced on relation " + rel.RelationName +", and changing this row will strand child rows.";
+								string delStr = "Cannot delete this row because constraints are enforced on relation " + rel.RelationName +", and deleting this row will strand child rows.";
+								string message = action == DataRowAction.Delete ? delStr : changeStr;
+								throw new InvalidConstraintException(message);
+							}
+						}
+					}
+					break;
+				case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
+					if (childRows != null)
+					{
+						for (int j = 0; j < childRows.Length; j++)
+						{
+							DataRow child = childRows[j];
+							if (childRows[j].RowState != DataRowState.Deleted)
+							{
+								//set only the key columns to default
+								for (int k = 0; k < rel.ChildColumns.Length; k++)
+									child[rel.ChildColumns[k]] = rel.ChildColumns[k].DefaultValue;
+							}
+						}
+					}
+					break;
+				case Rule.SetNull: // set the values in the child row to null.
+					if (childRows != null)
+					{
+						for (int j = 0; j < childRows.Length; j++)
+						{
+							DataRow child = childRows[j];
+							if (childRows[j].RowState != DataRowState.Deleted)
+							{
+								// set only the key columns to DBNull
+								for (int k = 0; k < rel.ChildColumns.Length; k++)
+									child.SetNull(rel.ChildColumns[k]);
+							}
+						}
+					}
+					break;
+			}
+
+		}
+
+		/// <summary>
+		/// Ends the edit occurring on the row.
+		/// </summary>
+		[MonoTODO]
+		public void EndEdit () 
+		{
+			editing = false;
+			if (rowState == DataRowState.Detached)
+				return;
+			if (HasVersion (DataRowVersion.Proposed))
+			{
+				_table.ChangingDataRow(this, DataRowAction.Change);
+				if (rowState == DataRowState.Unchanged)
+					rowState = DataRowState.Modified;
+				
+				//Calling next method validates UniqueConstraints
+				//and ForeignKeys.
+				try
+				{
+					if (_table.DataSet == null || _table.DataSet.EnforceConstraints)
+						_table.Rows.ValidateDataRowInternal(this);
+				}
+				catch (Exception e)
+				{
+					proposed = null;
+					throw e;
+				}
+				// check all child rows.
+				CheckChildRows(DataRowAction.Change);
+				current = proposed;
+				proposed = null;
+				_table.ChangedDataRow(this, DataRowAction.Change);
+			}
+		}
+
+		/// <summary>
+		/// Gets the child rows of this DataRow using the specified DataRelation.
+		/// </summary>
+		public DataRow[] GetChildRows (DataRelation relation) 
+		{
+			return GetChildRows (relation, DataRowVersion.Current);
+		}
+
+		/// <summary>
+		/// Gets the child rows of a DataRow using the specified RelationName of a
+		/// DataRelation.
+		/// </summary>
+		public DataRow[] GetChildRows (string relationName) 
+		{
+			return GetChildRows (Table.DataSet.Relations[relationName]);
+		}
+
+		/// <summary>
+		/// Gets the child rows of a DataRow using the specified DataRelation, and
+		/// DataRowVersion.
+		/// </summary>
+		public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
+		{
+			if (relation == null)
+				return new DataRow[0];
+
+			if (this.Table == null)
+				throw new RowNotInTableException();
+
+			if (relation.DataSet != this.Table.DataSet)
+				throw new ArgumentException();
+
+			// TODO: Caching for better preformance
+			ArrayList rows = new ArrayList();
+			DataColumn[] parentColumns = relation.ParentColumns;
+			DataColumn[] childColumns = relation.ChildColumns;
+			int numColumn = parentColumns.Length;
+			if (HasVersion(version)) 
+			{
+				foreach (DataRow row in relation.ChildTable.Rows) 
+				{
+					bool allColumnsMatch = false;
+					if (row.HasVersion(DataRowVersion.Default))
+					{
+						allColumnsMatch = true;
+						for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
+						{
+							if (!this[parentColumns[columnCnt], version].Equals(
+								row[childColumns[columnCnt], DataRowVersion.Default])) 
+							{
+								allColumnsMatch = false;
+								break;
+							}
+						}
+					}
+					if (allColumnsMatch) rows.Add(row);
+				}
+			}
+			return rows.ToArray(typeof(DataRow)) as DataRow[];
+		}
+
+		/// <summary>
+		/// Gets the child rows of a DataRow using the specified RelationName of a
+		/// DataRelation, and DataRowVersion.
+		/// </summary>
+		public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
+		{
+			return GetChildRows (Table.DataSet.Relations[relationName], version);
+		}
+
+		/// <summary>
+		/// Gets the error description of the specified DataColumn.
+		/// </summary>
+		public string GetColumnError (DataColumn column) 
+		{
+			return GetColumnError (_table.Columns.IndexOf(column));
+		}
+
+		/// <summary>
+		/// Gets the error description for the column specified by index.
+		/// </summary>
+		public string GetColumnError (int columnIndex) 
+		{
+			if (columnIndex < 0 || columnIndex >= columnErrors.Length)
+				throw new IndexOutOfRangeException ();
+
+			string retVal = columnErrors[columnIndex];
+			if (retVal == null)
+				retVal = string.Empty;
+			return retVal;
+		}
+
+		/// <summary>
+		/// Gets the error description for the column, specified by name.
+		/// </summary>
+		public string GetColumnError (string columnName) 
+		{
+			return GetColumnError (_table.Columns.IndexOf(columnName));
+		}
+
+		/// <summary>
+		/// Gets an array of columns that have errors.
+		/// </summary>
+		public DataColumn[] GetColumnsInError () 
+		{
+			ArrayList dataColumns = new ArrayList ();
+
+			for (int i = 0; i < columnErrors.Length; i += 1)
+			{
+				if (columnErrors[i] != null && columnErrors[i] != String.Empty)
+					dataColumns.Add (_table.Columns[i]);
+			}
+
+			return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
+		}
+
+		/// <summary>
+		/// Gets the parent row of a DataRow using the specified DataRelation.
+		/// </summary>
+		public DataRow GetParentRow (DataRelation relation) 
+		{
+			return GetParentRow (relation, DataRowVersion.Current);
+		}
+
+		/// <summary>
+		/// Gets the parent row of a DataRow using the specified RelationName of a
+		/// DataRelation.
+		/// </summary>
+		public DataRow GetParentRow (string relationName) 
+		{
+			return GetParentRow (relationName, DataRowVersion.Current);
+		}
+
+		/// <summary>
+		/// Gets the parent row of a DataRow using the specified DataRelation, and
+		/// DataRowVersion.
+		/// </summary>
+		public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
+		{
+			DataRow[] rows = GetParentRows(relation, version);
+			if (rows.Length == 0) return null;
+			return rows[0];
+		}
+
+		/// <summary>
+		/// Gets the parent row of a DataRow using the specified RelationName of a 
+		/// DataRelation, and DataRowVersion.
+		/// </summary>
+		public DataRow GetParentRow (string relationName, DataRowVersion version) 
+		{
+			return GetParentRow (Table.DataSet.Relations[relationName], version);
+		}
+
+		/// <summary>
+		/// Gets the parent rows of a DataRow using the specified DataRelation.
+		/// </summary>
+		public DataRow[] GetParentRows (DataRelation relation) 
+		{
+			return GetParentRows (relation, DataRowVersion.Current);
+		}
+
+		/// <summary>
+		/// Gets the parent rows of a DataRow using the specified RelationName of a 
+		/// DataRelation.
+		/// </summary>
+		public DataRow[] GetParentRows (string relationName) 
+		{
+			return GetParentRows (relationName, DataRowVersion.Current);
+		}
+
+		/// <summary>
+		/// Gets the parent rows of a DataRow using the specified DataRelation, and
+		/// DataRowVersion.
+		/// </summary>
+		public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
+		{
+			// TODO: Caching for better preformance
+			if (relation == null)
+				return new DataRow[0];
+
+			if (this.Table == null)
+				throw new RowNotInTableException();
+
+			if (relation.DataSet != this.Table.DataSet)
+				throw new ArgumentException();
+
+			ArrayList rows = new ArrayList();
+			DataColumn[] parentColumns = relation.ParentColumns;
+			DataColumn[] childColumns = relation.ChildColumns;
+			int numColumn = parentColumns.Length;
+			if (HasVersion(version))
+			{
+				foreach (DataRow row in relation.ParentTable.Rows) 
+				{
+					bool allColumnsMatch = false;
+					if (row.HasVersion(DataRowVersion.Default))
+					{
+						allColumnsMatch = true;
+						for (int columnCnt = 0; columnCnt < numColumn; columnCnt++) 
+						{
+							if (!this[childColumns[columnCnt], version].Equals(
+								row[parentColumns[columnCnt], DataRowVersion.Default])) 
+							{
+								allColumnsMatch = false;
+								break;
+							}
+						}
+					}
+					if (allColumnsMatch) rows.Add(row);
+				}
+			}
+			return rows.ToArray(typeof(DataRow)) as DataRow[];
+		}
+
+		/// <summary>
+		/// Gets the parent rows of a DataRow using the specified RelationName of a 
+		/// DataRelation, and DataRowVersion.
+		/// </summary>
+		public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
+		{
+			return GetParentRows (Table.DataSet.Relations[relationName], version);
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether a specified version exists.
+		/// </summary>
+		public bool HasVersion (DataRowVersion version) 
+		{
+			switch (version)
+			{
+				case DataRowVersion.Default:
+					if (rowState == DataRowState.Deleted)
+						return false;
+					if (rowState == DataRowState.Detached)
+						return proposed != null;
+					return true;
+				case DataRowVersion.Proposed:
+					if (rowState == DataRowState.Deleted)
+						return false;
+					return (proposed != null);
+				case DataRowVersion.Current:
+					if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
+						return false;
+					return (current != null);
+				case DataRowVersion.Original:
+					if (rowState == DataRowState.Detached)
+						return false;
+					return (original != null);
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether the specified DataColumn contains a null value.
+		/// </summary>
+		public bool IsNull (DataColumn column) 
+		{
+			object o = this[column];
+			return (o == null || o == DBNull.Value);
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether the column at the specified index contains a null
+		/// value.
+		/// </summary>
+		public bool IsNull (int columnIndex) 
+		{
+			object o = this[columnIndex];
+			return (o == null || o == DBNull.Value);
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether the named column contains a null value.
+		/// </summary>
+		public bool IsNull (string columnName) 
+		{
+			object o = this[columnName];
+			return (o == null || o == DBNull.Value);
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether the specified DataColumn and DataRowVersion
+		/// contains a null value.
+		/// </summary>
+		public bool IsNull (DataColumn column, DataRowVersion version) 
+		{
+			object o = this[column, version];
+			return (o == null || o == DBNull.Value);
+		}
+
+		/// <summary>
+		/// Rejects all changes made to the row since AcceptChanges was last called.
+		/// </summary>
+		public void RejectChanges () 
+		{
+			// If original is null, then nothing has happened since AcceptChanges
+			// was last called.  We have no "original" to go back to.
+			if (original != null)
+			{
+				Array.Copy (original, current, _table.Columns.Count);
+			       
+				_table.ChangedDataRow (this, DataRowAction.Rollback);
+				CancelEdit ();
+				switch (rowState)
+				{
+					case DataRowState.Added:
+						_table.Rows.Remove (this);
+						break;
+					case DataRowState.Modified:
+						rowState = DataRowState.Unchanged;
+						break;
+					case DataRowState.Deleted:
+						rowState = DataRowState.Unchanged;
+						break;
+				} 
+				
+			} 			
+			else {
+				// If rows are just loaded via Xml the original values are null.
+				// So in this case we have to remove all columns.
+				// FIXME: I'm not realy sure, does this break something else, but
+				// if so: FIXME ;)
+				
+				if ((rowState & DataRowState.Added) > 0)
+					_table.Rows.Remove (this);
+			}
+		}
+
+		/// <summary>
+		/// Sets the error description for a column specified as a DataColumn.
+		/// </summary>
+		public void SetColumnError (DataColumn column, string error) 
+		{
+			SetColumnError (_table.Columns.IndexOf (column), error);
+		}
+
+		/// <summary>
+		/// Sets the error description for a column specified by index.
+		/// </summary>
+		public void SetColumnError (int columnIndex, string error) 
+		{
+			if (columnIndex < 0 || columnIndex >= columnErrors.Length)
+				throw new IndexOutOfRangeException ();
+			columnErrors[columnIndex] = error;
+		}
+
+		/// <summary>
+		/// Sets the error description for a column specified by name.
+		/// </summary>
+		public void SetColumnError (string columnName, string error) 
+		{
+			SetColumnError (_table.Columns.IndexOf (columnName), error);
+		}
+
+		/// <summary>
+		/// Sets the value of the specified DataColumn to a null value.
+		/// </summary>
+		protected void SetNull (DataColumn column) 
+		{
+			this[column] = DBNull.Value;
+		}
+
+		/// <summary>
+		/// Sets the parent row of a DataRow with specified new parent DataRow.
+		/// </summary>
+		[MonoTODO]
+		public void SetParentRow (DataRow parentRow) 
+		{
+			SetParentRow(parentRow, null);
+		}
+
+		/// <summary>
+		/// Sets the parent row of a DataRow with specified new parent DataRow and
+		/// DataRelation.
+		/// </summary>
+		[MonoTODO]
+		public void SetParentRow (DataRow parentRow, DataRelation relation) 
+		{
+			if (_table == null || parentRow.Table == null)
+				throw new RowNotInTableException();
+
+			if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
+				throw new ArgumentException();
+			
+			BeginEdit();
+			if (relation == null)
+			{
+				foreach (DataRelation parentRel in _table.ParentRelations)
+				{
+					DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
+					DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
+					
+					for (int i = 0; i < parentCols.Length; i++)
+					{
+						if (parentRow == null)
+							this[childCols[i].Ordinal] = DBNull.Value;
+						else
+							this[childCols[i].Ordinal] = parentRow[parentCols[i]];
+					}
+					
+				}
+			}
+			else
+			{
+				DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
+				DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
+					
+				for (int i = 0; i < parentCols.Length; i++)
+				{
+					if (parentRow == null)
+						this[childCols[i].Ordinal] = DBNull.Value;
+					else
+						this[childCols[i].Ordinal] = parentRow[parentCols[i]];
+				}
+			}
+			EndEdit();
+		}
+		
+		//Copy all values of this DataaRow to the row parameter.
+		internal void CopyValuesToRow(DataRow row)
+		{
+						
+			if (row == null)
+				throw new ArgumentNullException("row");
+			if (row == this)
+				throw new ArgumentException("'row' is the same as this object");
+
+			DataColumnCollection columns = Table.Columns;
+			
+			for(int i = 0; i < columns.Count; i++){
+
+				string columnName = columns[i].ColumnName;
+				int index = row.Table.Columns.IndexOf(columnName);
+				//if a column with the same name exists in both rows copy the values
+				if(index != -1) {
+					if (HasVersion(DataRowVersion.Original))
+					{
+						if (row.original == null)
+							row.original = new object[row.Table.Columns.Count];
+						row.original[index] = row.SetColumnValue(original[i], index);
+					}
+					if (HasVersion(DataRowVersion.Current))
+					{
+						if (row.current == null)
+							row.current = new object[row.Table.Columns.Count];
+						row.current[index] = row.SetColumnValue(current[i], index);
+					}
+					if (HasVersion(DataRowVersion.Proposed))
+					{
+						if (row.proposed == null)
+							row.proposed = new object[row.Table.Columns.Count];
+						row.proposed[index] = row.SetColumnValue(proposed[i], index);
+					}
+					
+					//Saving the current value as the column value
+					row[index] = row.current[index];
+					
+				}
+			}
+
+			row.rowState = RowState;
+			row.RowError = RowError;
+			row.columnErrors = columnErrors;
+		}
+
+		
+		public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
+		{
+			// if a column is added we hava to add an additional value the 
+			// the priginal, current and propoed arrays.
+			// this scenario can happened in merge operation.
+
+			if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
+			{
+				object[] tmp;
+				if (current != null)
+				{
+					tmp = new object[current.Length + 1];
+					Array.Copy (current, tmp, current.Length);
+					tmp[tmp.Length - 1] = DBNull.Value;
+					current = tmp;
+				}
+				if (proposed != null)
+				{
+					tmp = new object[proposed.Length + 1];
+					Array.Copy (proposed, tmp, proposed.Length);
+					tmp[tmp.Length - 1] = DBNull.Value;
+					proposed = tmp;
+				}
+				if(original != null)
+				{
+					tmp = new object[original.Length + 1];
+					Array.Copy (original, tmp, original.Length);
+					tmp[tmp.Length - 1] = DBNull.Value;
+					original = tmp;
+				}
+
+			}
+		}
+
+		internal bool IsRowChanged(DataRowState rowState) {
+			if((RowState & rowState) != 0)
+				return true;
+
+			//we need to find if child rows of this row changed.
+			//if yes - we should return true
+
+			// if the rowState is deleted we should get the original version of the row
+			// else - we should get the current version of the row.
+			DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
+			int count = Table.ChildRelations.Count;
+			for (int i = 0; i < count; i++){
+				DataRelation rel = Table.ChildRelations[i];
+				DataRow[] childRows = GetChildRows(rel, version);
+				for (int j = 0; j < childRows.Length; j++){
+					if (childRows[j].IsRowChanged(rowState))
+						return true;
+				}
+			}
+
+			return false;
+		}
+
+		internal bool HasParentCollection
+		{
+			get
+			{
+				return _hasParentCollection;
+			}
+			set
+			{
+				_hasParentCollection = value;
+			}
+		}
+
+		#endregion // Methods
+	}
+
+	
+
+}

+ 10 - 5
mcs/class/System.Data/System.Data/DataRowCollection.cs

@@ -62,13 +62,18 @@ namespace System.Data
 			if (row == null)
 				throw new ArgumentNullException("row", "'row' argument cannot be null.");
 
+			if (row.Table != this.table)
+				throw new ArgumentException ("This row already belongs to another table.");
+
 			if (list.IndexOf(row) != -1)
 				throw new ArgumentException ("This row already belongs to this table.");
 			
-			if (table.DataSet == null || table.DataSet.EnforceConstraints)
+
+			if ((table.DataSet == null || table.DataSet.EnforceConstraints) && !table._duringDataLoad)
 				// we have to check that the new row doesn't colide with existing row
 				ValidateDataRowInternal(row);
 			
+			row.HasParentCollection = true;
 			list.Add (row);
 			row.AttachRow ();
 			row.Table.ChangedDataRow (row, DataRowAction.Add);
@@ -150,7 +155,7 @@ namespace System.Data
 				if (row.RowState != DataRowState.Deleted)
 				{
 					object primValue = row [primColumnName];
-					if (key == null) 
+					if (key == null) 
 					{
 						if (primValue == null)
 							return row;
@@ -191,12 +196,12 @@ namespace System.Data
 				if (row.RowState != DataRowState.Deleted)
 				{
 					bool eq = true;
-					for (int i = 0; i < keys.Length; i++) 
+					for (int i = 0; i < keys.Length; i++) 
 					{
 					
 						object primValue = row [primColumnNames [i]];
 						object keyValue = keys [i];
-						if (keyValue == null) 
+						if (keyValue == null) 
 						{
 							if (primValue == null)
 								return row;
@@ -206,7 +211,7 @@ namespace System.Data
 								       
 						newKey = Convert.ChangeType (keyValue, Type.GetTypeCode(primValue.GetType ()));
 
-						if (!primValue.Equals (newKey)) 
+						if (!primValue.Equals (newKey)) 
 						{
 							eq = false;
 							break;

+ 1339 - 1338
mcs/class/System.Data/System.Data/DataTable.cs

@@ -1,1338 +1,1339 @@
-//
-// System.Data.DataTable.cs
-//
-// Author:
-//   Franklin Wise <[email protected]>
-//   Christopher Podurgiel ([email protected])
-//   Daniel Morgan <[email protected]>
-//   Rodrigo Moya <[email protected]>
-//   Tim Coleman ([email protected])
-//   Ville Palo <[email protected]>
-//
-// (C) Chris Podurgiel
-// (C) Ximian, Inc 2002
-// Copyright (C) Tim Coleman, 2002
-// Copyright (C) Daniel Morgan, 2002-2003
-//
-
-using System;
-using System.Collections;
-using System.ComponentModel;
-using System.Globalization;
-using System.Runtime.Serialization;
-
-namespace System.Data {
-	//[Designer]
-	[ToolboxItem (false)]
-	[DefaultEvent ("RowChanging")]
-	[DefaultProperty ("TableName")]
-	[DesignTimeVisible (false)]
-	[Serializable]
-	public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable 
-	{
-		internal DataSet dataSet;   
-		
-		private bool _caseSensitive;
-		private DataColumnCollection _columnCollection;
-		private ConstraintCollection _constraintCollection;
-		private DataView _defaultView;
-
-		private string _displayExpression;
-		private PropertyCollection _extendedProperties;
-		private bool _hasErrors;
-		private CultureInfo _locale;
-		private int _minimumCapacity;
-		private string _nameSpace;
-		private DataRelationCollection _childRelations; 
-		private DataRelationCollection _parentRelations;
-		private string _prefix;
-		private DataColumn[] _primaryKey;
-		private DataRowCollection _rows;
-		private ISite _site;
-		private string _tableName;
-		private bool _containsListCollection;
-		private string _encodedTableName;
-		internal bool _duringDataLoad;
-		private bool dataSetPrevEnforceConstraints;
-
-
-		
-		// If CaseSensitive property is changed once it does not anymore follow owner DataSet's 
-		// CaseSensitive property. So when you lost you virginity it's gone for ever
-		private bool _virginCaseSensitive = true;
-
-		/// <summary>
-		/// Initializes a new instance of the DataTable class with no arguments.
-		/// </summary>
-		
-		public DataTable () 
-		{
-			dataSet = null;
-			_columnCollection = new DataColumnCollection(this);
-			_constraintCollection = new ConstraintCollection(this); 
-			_extendedProperties = new PropertyCollection();
-			_tableName = "";
-			_nameSpace = null;
-			_caseSensitive = false;  	//default value
-			_displayExpression = null;
-			_primaryKey = null;
-			_site = null;
-			_rows = new DataRowCollection (this);
-			
-			//LAMESPEC: spec says 25 impl does 50
-			_minimumCapacity = 50;
-			
-			_childRelations = new DataRelationCollection.DataTableRelationCollection (this);
-			_parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
-
-		
-			_defaultView = new DataView(this);
-		}
-
-		/// <summary>
-		/// Intitalizes a new instance of the DataTable class with the specified table name.
-		/// </summary>
-		public DataTable (string tableName) : this () 
-		{
-			_tableName = tableName;
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
-		/// </summary>
-		[MonoTODO]
-		protected DataTable (SerializationInfo info, StreamingContext context)
-			: this () 
-		{
-			//
-			// TODO: Add constructor logic here
-			//
-		}
-
-		/// <summary>
-		/// Indicates whether string comparisons within the table are case-sensitive.
-		/// </summary>
-		[DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]	
-		public bool CaseSensitive {
-			get { return _caseSensitive; }
-			set { 
-				_virginCaseSensitive = false;
-				_caseSensitive = value; 
-			}
-		}
-
-		internal bool VirginCaseSensitive {
-			get { return _virginCaseSensitive; }
-			set { _virginCaseSensitive = value; }
-		}
-
-		internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv) 
-		{
-			DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
-			OnColumnChanged(e);
-		}
-
-		internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv) 
-		{
-			DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
-			OnColumnChanging (e);
-		}
-
-		internal void DeletedDataRow (DataRow dr, DataRowAction action) 
-		{
-			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
-			OnRowDeleted (e);
-		}
-
-		internal void DeletingDataRow (DataRow dr, DataRowAction action) 
-		{
-			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
-			OnRowDeleting(e);
-		}
-
-		internal void ChangedDataRow (DataRow dr, DataRowAction action) 
-		{
-			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
-			OnRowChanged (e);
-		}
-
-		internal void ChangingDataRow (DataRow dr, DataRowAction action) 
-		{
-			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
-			OnRowChanging (e);
-		}
-
-		/// <summary>
-		/// Gets the collection of child relations for this DataTable.
-		/// </summary>
-		[Browsable (false)]
-		[DataSysDescription ("Returns the child relations for this table.")]
-		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-		public DataRelationCollection ChildRelations {
-			get {
-				return _childRelations;
-			}
-		}
-
-		/// <summary>
-		/// Gets the collection of columns that belong to this table.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("The collection that holds the columns for this table.")]
-		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
-		public DataColumnCollection Columns {
-			get { return _columnCollection; }
-		}
-
-		/// <summary>
-		/// Gets the collection of constraints maintained by this table.
-		/// </summary>
-		[DataCategory ("Data")]	
-		[DataSysDescription ("The collection that holds the constraints for this table.")]
-		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
-		public ConstraintCollection Constraints {
-			get { return _constraintCollection; }
-		}
-
-		/// <summary>
-		/// Gets the DataSet that this table belongs to.
-		/// </summary>
-		[Browsable (false)]
-		[DataSysDescription ("Indicates the DataSet to which this table belongs.")]
-		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-		public DataSet DataSet {
-			get { return dataSet; }
-		}
-
-		
-
-		/// <summary>
-		/// Gets a customized view of the table which may 
-		/// include a filtered view, or a cursor position.
-		/// </summary>
-		[MonoTODO]	
-		[Browsable (false)]
-		[DataSysDescription ("This is the default DataView for the table.")]
-		public DataView DefaultView {
-			get { return _defaultView; }
-		}
-		
-
-		/// <summary>
-		/// Gets or sets the expression that will return 
-		/// a value used to represent this table in the user interface.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("The expression used to compute the data-bound value of this row.")]	
-		[DefaultValue ("")]
-		public string DisplayExpression {
-			get { return "" + _displayExpression; }
-			set { _displayExpression = value; }
-		}
-
-		/// <summary>
-		/// Gets the collection of customized user information.
-		/// </summary>
-		[Browsable (false)]
-		[DataCategory ("Data")]
-		[DataSysDescription ("The collection that holds custom user information.")]
-		public PropertyCollection ExtendedProperties {
-			get { return _extendedProperties; }
-		}
-
-		/// <summary>
-		/// Gets a value indicating whether there are errors in 
-		/// any of the_rows in any of the tables of the DataSet to 
-		/// which the table belongs.
-		/// </summary>
-		[Browsable (false)]
-		[DataSysDescription ("Returns whether the table has errors.")]
-		public bool HasErrors {
-			get { 
-				// we can not use the _hasError flag because we do not know when to turn it off!
-				for (int i = 0; i < _rows.Count; i++)
-				{
-					if (_rows[i].HasErrors)
-						return true;
-				}
-				return false;
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the locale information used to 
-		/// compare strings within the table.
-		/// </summary>
-		[DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
-		public CultureInfo Locale {
-			get { 
-				// if the locale is null, we check for the DataSet locale
-				// and if the DataSet is null we return the current culture.
-				// this way if DataSet locale is changed, only if there is no locale for 
-				// the DataTable it influece the Locale get;
-				if (_locale != null)
-					return _locale;
-				if (DataSet != null)
-					return DataSet.Locale;
-				return CultureInfo.CurrentCulture;
-			}
-			set { 
-				if (_locale == null || !_locale.Equals(value))
-					_locale = value; 
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the initial starting size for this table.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("Indicates an initial starting size for this table.")]
-		[DefaultValue (50)]
-		public int MinimumCapacity {
-			get { return _minimumCapacity; }
-			set { _minimumCapacity = value; }
-		}
-
-		/// <summary>
-		/// Gets or sets the namespace for the XML represenation 
-		/// of the data stored in the DataTable.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
-		public string Namespace {
-			get { return "" + _nameSpace; }
-			set { _nameSpace = value; }
-		}
-
-		/// <summary>
-		/// Gets the collection of parent relations for 
-		/// this DataTable.
-		/// </summary>
-		[Browsable (false)]
-		[DataSysDescription ("Returns the parent relations for this table.")]
-		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-		public DataRelationCollection ParentRelations {
-			get {	
-				return _parentRelations;
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the namespace for the XML represenation
-		///  of the data stored in the DataTable.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
-		[DefaultValue ("")]
-		public string Prefix {
-			get { return "" + _prefix; }
-			set { _prefix = value; }
-		}
-
-		/// <summary>
-		/// Gets or sets an array of columns that function as 
-		/// primary keys for the data table.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
-		public DataColumn[] PrimaryKey {
-			get {
-				UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
-				if (null == uc) return new DataColumn[] {};
-				return uc.Columns;
-			}
-			set {
-
-				//YUK: msft removes a previous unique constraint if it is flagged as a pk  
-				//when a new pk is set 
-				
-				//clear Primary Key if value == null
-				if (null == value) {
-					
-					RemoveUniqueConstraints ();
-					return;
-				}
-
-				//Does constraint exist for these columns
-				UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
-					this.Constraints, (DataColumn[]) value);
-
-				//if constraint doesn't exist for columns
-				//create new unique primary key constraint
-				if (null == uc) {
-
-					RemoveUniqueConstraints ();						
-					
-					foreach (DataColumn Col in (DataColumn[]) value) {
-
-						if (Col.Table == null)
-							break;
-
-						if (Columns.IndexOf (Col) < 0)
-							throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
-					}
-
-
-					uc = new UniqueConstraint( (DataColumn[]) value, true);
-					
-					Constraints.Add (uc);
-				}
-				else { //set existing constraint as the new primary key
-					UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
-				}
-				
-			}
-		}
-
-		/// <summary>
-		/// Gets the collection of_rows that belong to this table.
-		/// </summary>
-		[Browsable (false)]
-		[DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]	
-		public DataRowCollection Rows {
-			get { return _rows; }
-		}
-
-		/// <summary>
-		/// Gets or sets an System.ComponentModel.ISite 
-		/// for the DataTable.
-		/// </summary>
-		[Browsable (false)]
-		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-		public override ISite Site {
-			get { return _site; }
-			set { _site = value; }
-		}
-
-		/// <summary>
-		/// Gets or sets the name of the the DataTable.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
-		[DefaultValue ("")]	
-		[RefreshProperties (RefreshProperties.All)]
-		public string TableName {
-			get { return "" + _tableName; }
-			set { _tableName = value; }
-		}
-		
-		bool IListSource.ContainsListCollection {
-			get {
-				// the collection is a DataView
-				return false;
-			}
-		}
-
-		/// <summary>
-		/// Commits all the changes made to this table since the 
-		/// last time AcceptChanges was called.
-		/// </summary>
-		public void AcceptChanges () 
-		{
-			//FIXME: Do we need to validate anything here or
-			//try to catch any errors to deal with them?
-			
-			// we do not use foreach because if one of the rows is in Delete state
-			// it will be romeved from Rows and we get an exception.
-			DataRow myRow;
-			for (int i = 0; i < Rows.Count; )
-			{
-				myRow = Rows[i];
-				myRow.AcceptChanges();
-
-				// if the row state is Detached it meens that it was removed from row list (Rows)
-				// so we should not increase 'i'.
-				if (myRow.RowState != DataRowState.Detached)
-					i++;
-			}
-		}
-
-		/// <summary>
-		/// Begins the initialization of a DataTable that is used 
-		/// on a form or used by another component. The initialization
-		/// occurs at runtime.
-		/// </summary>
-		public void BeginInit () 
-		{
-		}
-
-		/// <summary>
-		/// Turns off notifications, index maintenance, and 
-		/// constraints while loading data.
-		/// </summary>
-		[MonoTODO]
-		public void BeginLoadData () 
-		{
-			if (!this._duringDataLoad)
-			{
-				//duringDataLoad is important to EndLoadData and
-				//for not throwing unexpected exceptions.
-				this._duringDataLoad = true;
-			
-				if (this.dataSet != null)
-				{
-					//Saving old Enforce constraints state for later
-					//use in the EndLoadData.
-					this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
-					this.dataSet.EnforceConstraints = false;
-				}
-			}
-			return;
-		}
-
-		/// <summary>
-		/// Clears the DataTable of all data.
-		/// </summary>
-		public void Clear () {
-			// TODO: thow an exception if any rows that 
-			//       have enforced child relations 
-			//       that would result in child rows being orphaned
-			// now we check if any ForeignKeyConstraint is referncing 'table'.
-			if (DataSet != null)
-			{
-				IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
-			
-				// loop on all tables in dataset
-				while (tableEnumerator.MoveNext())
-				{
-					IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
-					// loop on all constrains in the current table
-					while (constraintEnumerator.MoveNext())
-					{
-						Object o = constraintEnumerator.Current;
-						// we only check ForeignKeyConstraint
-						if (o is ForeignKeyConstraint)
-						{
-							ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
-							if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
-								throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
-						}
-					}
-				}
-			}
-			
-			// TODO: throw a NotSupportedException if the DataTable is part
-			//       of a DataSet that is binded to an XmlDataDocument
-			
-			_rows.Clear ();
-		}
-
-		/// <summary>
-		/// Clones the structure of the DataTable, including
-		///  all DataTable schemas and constraints.
-		/// </summary>
-		[MonoTODO]
-		public virtual DataTable Clone () 
-		{
-			DataTable Copy = new DataTable ();			
-			
-			CopyProperties (Copy);
-			return Copy;
-		}
-
-		/// <summary>
-		/// Computes the given expression on the current_rows that 
-		/// pass the filter criteria.
-		/// </summary>
-		[MonoTODO]
-		public object Compute (string expression, string filter) 
-		{
-			// TODO: implement this function based
-			//       on Select (expression)
-			//
-			// expression is an aggregate function
-			// filter is an expression used to limit rows
-
-			object obj = null;
-
-			// filter rows
-			ExpressionElement Expression = new ExpressionMainElement (filter);
-			
-			ArrayList List = new ArrayList ();
-			foreach (DataRow Row in Rows) {
-				
-				if (Expression.Test (Row))
-					List.Add (Row);
-			}
-			
-			DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
-
-			// TODO: with the filtered rows, execute the aggregate function
-			//       mentioned in expression
-
-			return obj;
-		}
-
-		/// <summary>
-		/// Copies both the structure and data for this DataTable.
-		/// </summary>
-		[MonoTODO]	
-		public DataTable Copy () 
-		{
-			DataTable Copy = new DataTable ();
-			CopyProperties (Copy);
-
-			// we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
-			// because if the row state is deleted we get an exception.
-			foreach (DataRow Row in Rows) {
-				DataRow NewRow = Copy.NewRow ();
-				Copy.Rows.Add (NewRow);
-				Row.CopyValuesToRow(NewRow);
-			}
-		       			
-			return Copy;
-		}
-
-		[MonoTODO]
-		private void CopyProperties (DataTable Copy) 
-		{
-					
-			Copy.CaseSensitive = CaseSensitive;
-			Copy.VirginCaseSensitive = VirginCaseSensitive;
-
-			// Copy.ChildRelations
-			// Copy.Constraints
-			// Copy.Container
-			// Copy.DefaultView
-			// Copy.DesignMode
-			Copy.DisplayExpression = DisplayExpression;
-			// Copy.ExtendedProperties
-			Copy.Locale = Locale;
-			Copy.MinimumCapacity = MinimumCapacity;
-			Copy.Namespace = Namespace;
-			// Copy.ParentRelations
-			Copy.Prefix = Prefix;
-			Copy.Site = Site;
-			Copy.TableName = TableName;
-
-
-
-			// Copy columns
-			foreach (DataColumn Column in Columns) {
-				
-				Copy.Columns.Add (CopyColumn (Column));	
-			}
-
-			CopyConstraints(Copy);
-			// add primary key to the copy
-			if (PrimaryKey.Length > 0)
-			{
-				DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
-				for (int i = 0; i < pColumns.Length; i++)
-					pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
-
-				Copy.PrimaryKey = pColumns;
-			}
-		}
-
-		private void CopyConstraints(DataTable copy)
-		{
-			UniqueConstraint origUc;
-			UniqueConstraint copyUc;
-			for (int i = 0; i < this.Constraints.Count; i++)
-			{
-				if (this.Constraints[i] is UniqueConstraint)
-				{
-					origUc = (UniqueConstraint)this.Constraints[i];
-					DataColumn[] columns = new DataColumn[origUc.Columns.Length];
-					for (int j = 0; j < columns.Length; j++)
-						columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
-					
-					copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
-					copy.Constraints.Add(copyUc);
-				}
-			}
-		}
-		/// <summary>
-		/// Ends the initialization of a DataTable that is used 
-		/// on a form or used by another component. The 
-		/// initialization occurs at runtime.
-		/// </summary>
-		[MonoTODO]
-		public void EndInit () 
-		{
-
-		}
-
-		/// <summary>
-		/// Turns on notifications, index maintenance, and 
-		/// constraints after loading data.
-		/// </summary>
-		[MonoTODO]
-		public void EndLoadData() 
-		{
-			int i = 0;
-			if (this._duringDataLoad) 
-			{
-				//Returning from loading mode, raising exceptions as usual
-				this._duringDataLoad = false;
-				
-				if (this.dataSet !=null)
-				{
-					//Getting back to previous EnforceConstraint state
-					this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
-				}
-				for (i=0 ; i<this.Rows.Count ; i++)
-				{
-					if (this.Rows[i]._nullConstraintViolation )
-					{
-						throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
-					}
-						
-				}
-
-			}
-
-		}
-
-		/// <summary>
-		/// Gets a copy of the DataTable that contains all
-		///  changes made to it since it was loaded or 
-		///  AcceptChanges was last called.
-		/// </summary>
-		[MonoTODO]
-		public DataTable GetChanges() 
-		{
-			return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
-		}
-
-		/// <summary>
-		/// Gets a copy of the DataTable containing all 
-		/// changes made to it since it was last loaded, or 
-		/// since AcceptChanges was called, filtered by DataRowState.
-		/// </summary>
-		[MonoTODO]	
-		public DataTable GetChanges(DataRowState rowStates) 
-		{
-			DataTable copyTable = null;
-
-			IEnumerator rowEnumerator = Rows.GetEnumerator();
-			while (rowEnumerator.MoveNext()) {
-				DataRow row = (DataRow)rowEnumerator.Current;
-				if (row.IsRowChanged(rowStates)) {
-					if (copyTable == null)
-						copyTable = Clone();
-					DataRow newRow = copyTable.NewRow();
-					copyTable.Rows.Add(newRow);
-					row.CopyValuesToRow(newRow);
-				}
-			}
-			 
-			return copyTable;
-		}
-
-		/// <summary>
-		/// Gets an array of DataRow objects that contain errors.
-		/// </summary>
-		[MonoTODO]
-		public DataRow[] GetErrors () 
-		{
-			ArrayList errors = new ArrayList();
-			for (int i = 0; i < _rows.Count; i++)
-			{
-				if (_rows[i].HasErrors)
-					errors.Add(_rows[i]);
-			}
-			
-			return (DataRow[]) errors.ToArray(typeof(DataRow));
-		}
-	
-		/// <summary>
-		/// This member is only meant to support Mono's infrastructure 
-		/// </summary>
-		protected virtual DataTable CreateInstance () 
-		{
-			return Activator.CreateInstance (this.GetType (), true) as DataTable;
-		}
-
-		/// <summary>
-		/// This member is only meant to support Mono's infrastructure 
-		/// </summary>
-		protected virtual Type GetRowType () 
-		{
-			return typeof (DataRow);
-		}
-
-		/// <summary>
-		/// This member is only meant to support Mono's infrastructure 
-		/// 
-		/// Used for Data Binding between System.Web.UI. controls 
-		/// like a DataGrid
-		/// or
-		/// System.Windows.Forms controls like a DataGrid
-		/// </summary>
-		IList IListSource.GetList () 
-		{
-			IList list = (IList) _defaultView;
-			return list;
-		}
-				
-		/// <summary>
-		/// Copies a DataRow into a DataTable, preserving any 
-		/// property settings, as well as original and current values.
-		/// </summary>
-		[MonoTODO]
-		public void ImportRow (DataRow row) 
-		{
-			DataRow newRow = NewRow();
-			Rows.Add(newRow);
-			row.CopyValuesToRow(newRow);
-			
-		}
-
-		/// <summary>
-		/// This member is only meant to support Mono's infrastructure 		
-		/// </summary>
-		[MonoTODO]
-		void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
-		{
-		}
-
-		/// <summary>
-		/// Finds and updates a specific row. If no matching row
-		///  is found, a new row is created using the given values.
-		/// </summary>
-		[MonoTODO]
-		public DataRow LoadDataRow (object[] values, bool fAcceptChanges) 
-		{
-			DataRow row = null;
-			if (PrimaryKey.Length == 0) {
-				row = Rows.Add (values);
-				if (fAcceptChanges)
-					row.AcceptChanges ();
-			}
-			else {
-				bool hasPrimaryValues = true;
-				// initiate an array that has the values of the primary keys.
-				object[] keyValues = new object[PrimaryKey.Length];
-				for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
-				{
-					if(PrimaryKey[i].Ordinal < values.Length)
-						keyValues[i] = values[PrimaryKey[i].Ordinal];
-					else
-						hasPrimaryValues = false;
-				}
-				
-				if (hasPrimaryValues){
-					// find the row in the table.
-					row = Rows.Find(keyValues);
-				}
-				
-				if (row == null)
-					row = Rows.Add (values);
-				else
-					row.ItemArray = values;
-				
-				if (fAcceptChanges)
-					row.AcceptChanges ();
-			}
-				
-			return row;
-		}
-
-		/// <summary>
-		/// Creates a new DataRow with the same schema as the table.
-		/// </summary>
-		public DataRow NewRow () 
-		{
-			return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
-		}
-
-		/// <summary>
-		/// This member supports the .NET Framework infrastructure
-		///  and is not intended to be used directly from your code.
-		/// </summary>
-		protected internal DataRow[] NewRowArray (int size) 
-		{
-			return (DataRow[]) Array.CreateInstance (GetRowType (), size);
-		}
-
-		/// <summary>
-		/// Creates a new row from an existing row.
-		/// </summary>
-		protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) 
-		{
-			return new DataRow (builder);
-		}
-	
-
-		/// <summary>
-		/// Rolls back all changes that have been made to the 
-		/// table since it was loaded, or the last time AcceptChanges
-		///  was called.
-		/// </summary>
-		[MonoTODO]
-		public void RejectChanges () 
-		{	
-			for (int i = _rows.Count - 1; i >= 0; i--) {
-				DataRow row = _rows [i];
-				if (row.RowState != DataRowState.Unchanged)
-					_rows [i].RejectChanges ();
-			}
-		}
-
-		/// <summary>
-		/// Resets the DataTable to its original state.
-		/// </summary>		
-		[MonoTODO]
-		public virtual void Reset () 
-		{
-			Clear();
-			while (ParentRelations.Count > 0)
-			{
-				if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
-					dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
-			}
-
-			while (ChildRelations.Count > 0)
-			{
-				if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
-					dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
-			}
-			Constraints.Clear();
-			Columns.Clear();
-		}
-
-		/// <summary>
-		/// Gets an array of all DataRow objects.
-		/// </summary>
-		public DataRow[] Select () 
-		{
-			return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
-		}
-
-		/// <summary>
-		/// Gets an array of all DataRow objects that match 
-		/// the filter criteria in order of primary key (or 
-		/// lacking one, order of addition.)
-		/// </summary>
-		public DataRow[] Select (string filterExpression) 
-		{
-			return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
-		}
-
-		/// <summary>
-		/// Gets an array of all DataRow objects that 
-		/// match the filter criteria, in the the 
-		/// specified sort order.
-		/// </summary>
-		public DataRow[] Select (string filterExpression, string sort) 
-		{
-			return Select(filterExpression, sort, DataViewRowState.CurrentRows);
-		}
-
-		/// <summary>
-		/// Gets an array of all DataRow objects that match
-		/// the filter in the order of the sort, that match 
-		/// the specified state.
-		/// </summary>
-		[MonoTODO]
-		public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) 
-		{
-			DataRow[] dataRows = null;
-			if (filterExpression == null)
-				filterExpression = String.Empty;
-
-			ExpressionElement Expression = null;
-			if (filterExpression != null && filterExpression.Length != 0)
-				Expression = new ExpressionMainElement(filterExpression);
-
-			ArrayList List = new ArrayList();
-			int recordStateFilter = GetRowStateFilter(recordStates);
-			foreach (DataRow Row in Rows) 
-			{
-				if (((int)Row.RowState & recordStateFilter) != 0)
-				{
-					if (Expression == null || Expression.Test(Row))
-						List.Add(Row);
-				}
-			}
-
-			dataRows = (DataRow[])List.ToArray(typeof(DataRow));
-			
-
-			if (sort != null && !sort.Equals(String.Empty)) 
-			{
-				SortableColumn[] sortableColumns = null;
-
-				sortableColumns = ParseTheSortString (sort);
-				if (sortableColumns == null)
-					throw new Exception ("sort expression result is null");
-				if (sortableColumns.Length == 0)
-					throw new Exception("sort expression result is 0");
-
-				RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
-				dataRows = rowSorter.SortRows ();
-
-				sortableColumns = null;
-				rowSorter = null;
-			}
-
-			
-			return dataRows;
-		}
-
-		private static int GetRowStateFilter(DataViewRowState recordStates)
-		{
-			int flag = 0;
-
-			if ((recordStates & DataViewRowState.Added) != 0)
-				flag |= (int)DataRowState.Added;
-			if ((recordStates & DataViewRowState.Deleted) != 0)
-				flag |= (int)DataRowState.Deleted;
-			if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
-				flag |= (int)DataRowState.Modified;
-			if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
-				flag |= (int)DataRowState.Modified;
-			if ((recordStates & DataViewRowState.Unchanged) != 0)
-				flag |= (int)DataRowState.Unchanged;
-
-			return flag;
-		}
-
-		/// <summary>
-		/// Gets the TableName and DisplayExpression, if 
-		/// there is one as a concatenated string.
-		/// </summary>
-		public override string ToString() 
-		{
-			//LAMESPEC: spec says concat the two. impl puts a 
-			//plus sign infront of DisplayExpression
-			string retVal = TableName;
-			if(DisplayExpression != null && DisplayExpression != "")
-				retVal += " + " + DisplayExpression;
-			return retVal;
-		}
-
-		
-		#region Events /////////////////
-		
-		/// <summary>
-		/// Raises the ColumnChanged event.
-		/// </summary>
-		protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
-			if (null != ColumnChanged) {
-				ColumnChanged (this, e);
-			}
-		}
-
-		/// <summary>
-		/// Raises the ColumnChanging event.
-		/// </summary>
-		protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
-			if (null != ColumnChanging) {
-				ColumnChanging (this, e);
-			}
-		}
-
-		/// <summary>
-		/// Raises the PropertyChanging event.
-		/// </summary>
-		[MonoTODO]
-		protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
-			//	if (null != PropertyChanging)
-			//	{
-			//		PropertyChanging (this, e);
-			//	}
-		}
-
-		/// <summary>
-		/// Notifies the DataTable that a DataColumn is being removed.
-		/// </summary>
-		[MonoTODO]
-		protected internal virtual void OnRemoveColumn (DataColumn column) {
-			//	if (null != RemoveColumn)
-			//	{
-			//		RemoveColumn(this, e);
-			//	}
-		}
-
-		/// <summary>
-		/// Raises the RowChanged event.
-		/// </summary>
-		protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
-			if (null != RowChanged) {
-				RowChanged(this, e);
-			}
-		}
-
-
-		/// <summary>
-		/// Raises the RowChanging event.
-		/// </summary>
-		protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
-			if (null != RowChanging) {
-				RowChanging(this, e);
-			}
-		}
-
-		/// <summary>
-		/// Raises the RowDeleted event.
-		/// </summary>
-		protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
-			if (null != RowDeleted) {
-				RowDeleted(this, e);
-			}
-		}
-
-		/// <summary>
-		/// Raises the RowDeleting event.
-		/// </summary>
-		protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
-			if (null != RowDeleting) {
-				RowDeleting(this, e);
-			}
-		}
-
-		[MonoTODO]
-		private DataColumn CopyColumn (DataColumn Column) {
-			DataColumn Copy = new DataColumn ();
-
-			// Copy all the properties of column
-			Copy.AllowDBNull = Column.AllowDBNull;
-			Copy.AutoIncrement = Column.AutoIncrement;
-			Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
-			Copy.AutoIncrementStep = Column.AutoIncrementStep;
-			Copy.Caption = Column.Caption;
-			Copy.ColumnMapping = Column.ColumnMapping;
-			Copy.ColumnName = Column.ColumnName;
-			//Copy.Container
-			Copy.DataType = Column.DataType;
-			Copy.DefaultValue = Column.DefaultValue;			
-			Copy.Expression = Column.Expression;
-			//Copy.ExtendedProperties
-			Copy.MaxLength = Column.MaxLength;
-			Copy.Namespace = Column.Namespace;
-			Copy.Prefix = Column.Prefix;
-			Copy.ReadOnly = Column.ReadOnly;
-			//Copy.Site
-			//we do not copy the unique value - it will be copyied when copying the constraints.
-			//Copy.Unique = Column.Unique;
-			
-			return Copy;
-		}			
-
-		/// <summary>
-		/// Occurs when after a value has been changed for 
-		/// the specified DataColumn in a DataRow.
-		/// </summary>
-		[DataCategory ("Data")]	
-		[DataSysDescription ("Occurs when a value has been changed for this column.")]
-		public event DataColumnChangeEventHandler ColumnChanged;
-
-		/// <summary>
-		/// Occurs when a value is being changed for the specified 
-		/// DataColumn in a DataRow.
-		/// </summary>
-		[DataCategory ("Data")]
-		[DataSysDescription ("Occurs when a value has been submitted for this column. The user can modify the proposed value and should throw an exception to cancel the edit.")]
-		public event DataColumnChangeEventHandler ColumnChanging;
-
-		/// <summary>
-		/// Occurs after a DataRow has been changed successfully.
-		/// </summary>
-		[DataCategory ("Data")]	
-		[DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
-		public event DataRowChangeEventHandler RowChanged;
-
-		/// <summary>
-		/// Occurs when a DataRow is changing.
-		/// </summary>
-		[DataCategory ("Data")]	
-		[DataSysDescription ("Occurs when the row is being changed so that the event handler can modify or cancel the change. The user can modify values in the row and should throw an  exception to cancel the edit.")]
-		public event DataRowChangeEventHandler RowChanging;
-
-		/// <summary>
-		/// Occurs after a row in the table has been deleted.
-		/// </summary>
-		[DataCategory ("Data")]	
-		[DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] 
-		public event DataRowChangeEventHandler RowDeleted;
-
-		/// <summary>
-		/// Occurs before a row in the table is about to be deleted.
-		/// </summary>
-		[DataCategory ("Data")]	
-		[DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
-		public event DataRowChangeEventHandler RowDeleting;
-		
-		#endregion // Events
-
-		/// <summary>
-		///  Removes all UniqueConstraints
-		/// </summary>
-		private void RemoveUniqueConstraints () 
-		{
-			foreach (Constraint Cons in Constraints) {
-				
-				if (Cons is UniqueConstraint) {
-					Constraints.Remove (Cons);
-					break;
-				}
-			}
-			
-			UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
-		}
-
-		// to parse the sort string for DataTable:Select(expression,sort)
-		// into sortable columns (think ORDER BY, 
-		// such as, "customer ASC, price DESC" )
-		private SortableColumn[] ParseTheSortString (string sort) 
-		{
-			SortableColumn[] sortColumns = null;
-			ArrayList columns = null;
-		
-			if (sort != null && !sort.Equals ("")) {
-				columns = new ArrayList ();
-				string[] columnExpression = sort.Trim ().Split (new char[1] {','});
-			
-				for (int c = 0; c < columnExpression.Length; c++) {
-					string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
-				
-					string columnName = columnSortInfo[0].Trim ();
-					string sortOrder = "ASC";
-					if (columnSortInfo.Length > 1) 
-						sortOrder = columnSortInfo[1].Trim ().ToUpper ();
-					
-					ListSortDirection sortDirection = ListSortDirection.Ascending;
-					switch (sortOrder) {
-					case "ASC":
-						sortDirection = ListSortDirection.Ascending;
-						break;
-					case "DESC":
-						sortDirection = ListSortDirection.Descending;
-						break;
-					default:
-						throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
-					}
-					Int32 ord = 0;
-					try {
-						ord = Int32.Parse (columnName);
-					}
-					catch (FormatException) {
-						ord = -1;
-					}
-					DataColumn dc = null;
-					if (ord == -1)				
-						dc = _columnCollection[columnName];
-					else
-						dc = _columnCollection[ord];
-					SortableColumn sortCol = new SortableColumn (dc,sortDirection);
-					columns.Add (sortCol);
-				}	
-				sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
-			}		
-			return sortColumns;
-		}
-	
-		private class SortableColumn 
-		{
-			private DataColumn col;
-			private ListSortDirection dir;
-
-			internal SortableColumn (DataColumn column, 
-						ListSortDirection direction) 
-			{
-				col = column;
-				dir = direction;
-			}
-
-			public DataColumn Column {
-				get {
-					return col;
-				}
-			}
-
-			public ListSortDirection SortDirection {
-				get {
-					return dir;
-				}
-			}
-		}
-
-		private class RowSorter : IComparer 
-		{
-			private SortableColumn[] sortColumns;
-			private DataRow[] rowsToSort;
-			
-			internal RowSorter(DataRow[] unsortedRows, 
-					SortableColumn[] sortColumns) 
-			{
-				this.sortColumns = sortColumns;
-				this.rowsToSort = unsortedRows;
-			}
-
-			public SortableColumn[] SortColumns {
-				get {
-					return sortColumns;
-				}
-			}
-			
-			public DataRow[] SortRows () 
-			{
-				Array.Sort (rowsToSort, this);
-				return rowsToSort;
-			}
-
-			int IComparer.Compare (object x, object y) 
-			{
-				if(x == null)
-					throw new Exception ("Object to compare is null: x");
-				if(y == null)
-					throw new Exception ("Object to compare is null: y");
-				if(!(x is DataRow))
-					throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
-				if(!(y is DataRow))
-					throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
-
-				DataRow rowx = (DataRow) x;
-				DataRow rowy = (DataRow) y;
-
-				for(int i = 0; i < sortColumns.Length; i++) {
-					SortableColumn sortColumn = sortColumns[i];
-					DataColumn dc = sortColumn.Column;
-
-					IComparable objx = (IComparable) rowx[dc];
-					object objy = rowy[dc];
-
-					int result = CompareObjects (objx, objy);
-					if (result != 0) {
-						if (sortColumn.SortDirection == ListSortDirection.Ascending) {
-							return result;
-						}
-						else {
-							return -result;
-						}
-					}
-				}
-				return 0;
-			}
-
-			private int CompareObjects (object a, object b) 
-			{
-				if (a == b)
-					return 0;
-				else if (a == null)
-					return -1;
-				else if (a == DBNull.Value)
-					return -1;
-				else if (b == null)
-					return 1;
-				else if (b == DBNull.Value)
-					return 1;
-
-				if((a is string) && (b is string)) {
-					a = ((string) a).ToUpper ();
-					b = ((string) b).ToUpper ();			
-				}
-
-				if (a is IComparable)
-					return ((a as IComparable).CompareTo (b));
-				else if (b is IComparable)
-					return -((b as IComparable).CompareTo (a));
-
-				throw new ArgumentException ("Neither a nor b IComparable");
-			}
-		}
-	}
-}
+//
+// System.Data.DataTable.cs
+//
+// Author:
+//   Franklin Wise <[email protected]>
+//   Christopher Podurgiel ([email protected])
+//   Daniel Morgan <[email protected]>
+//   Rodrigo Moya <[email protected]>
+//   Tim Coleman ([email protected])
+//   Ville Palo <[email protected]>
+//
+// (C) Chris Podurgiel
+// (C) Ximian, Inc 2002
+// Copyright (C) Tim Coleman, 2002
+// Copyright (C) Daniel Morgan, 2002-2003
+//
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace System.Data {
+	//[Designer]
+	[ToolboxItem (false)]
+	[DefaultEvent ("RowChanging")]
+	[DefaultProperty ("TableName")]
+	[DesignTimeVisible (false)]
+	[Serializable]
+	public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable 
+	{
+		internal DataSet dataSet;   
+		
+		private bool _caseSensitive;
+		private DataColumnCollection _columnCollection;
+		private ConstraintCollection _constraintCollection;
+		private DataView _defaultView;
+
+		private string _displayExpression;
+		private PropertyCollection _extendedProperties;
+		private bool _hasErrors;
+		private CultureInfo _locale;
+		private int _minimumCapacity;
+		private string _nameSpace;
+		private DataRelationCollection _childRelations; 
+		private DataRelationCollection _parentRelations;
+		private string _prefix;
+		private DataColumn[] _primaryKey;
+		private DataRowCollection _rows;
+		private ISite _site;
+		private string _tableName;
+		private bool _containsListCollection;
+		private string _encodedTableName;
+		internal bool _duringDataLoad;
+		private bool dataSetPrevEnforceConstraints;
+
+
+		
+		// If CaseSensitive property is changed once it does not anymore follow owner DataSet's 
+		// CaseSensitive property. So when you lost you virginity it's gone for ever
+		private bool _virginCaseSensitive = true;
+
+		/// <summary>
+		/// Initializes a new instance of the DataTable class with no arguments.
+		/// </summary>
+		
+		public DataTable () 
+		{
+			dataSet = null;
+			_columnCollection = new DataColumnCollection(this);
+			_constraintCollection = new ConstraintCollection(this); 
+			_extendedProperties = new PropertyCollection();
+			_tableName = "";
+			_nameSpace = null;
+			_caseSensitive = false;  	//default value
+			_displayExpression = null;
+			_primaryKey = null;
+			_site = null;
+			_rows = new DataRowCollection (this);
+			
+			//LAMESPEC: spec says 25 impl does 50
+			_minimumCapacity = 50;
+			
+			_childRelations = new DataRelationCollection.DataTableRelationCollection (this);
+			_parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
+
+		
+			_defaultView = new DataView(this);
+		}
+
+		/// <summary>
+		/// Intitalizes a new instance of the DataTable class with the specified table name.
+		/// </summary>
+		public DataTable (string tableName) : this () 
+		{
+			_tableName = tableName;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
+		/// </summary>
+		[MonoTODO]
+		protected DataTable (SerializationInfo info, StreamingContext context)
+			: this () 
+		{
+			//
+			// TODO: Add constructor logic here
+			//
+		}
+
+		/// <summary>
+		/// Indicates whether string comparisons within the table are case-sensitive.
+		/// </summary>
+		[DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]	
+		public bool CaseSensitive {
+			get { return _caseSensitive; }
+			set { 
+				_virginCaseSensitive = false;
+				_caseSensitive = value; 
+			}
+		}
+
+		internal bool VirginCaseSensitive {
+			get { return _virginCaseSensitive; }
+			set { _virginCaseSensitive = value; }
+		}
+
+		internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv) 
+		{
+			DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
+			OnColumnChanged(e);
+		}
+
+		internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv) 
+		{
+			DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
+			OnColumnChanging (e);
+		}
+
+		internal void DeletedDataRow (DataRow dr, DataRowAction action) 
+		{
+			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
+			OnRowDeleted (e);
+		}
+
+		internal void DeletingDataRow (DataRow dr, DataRowAction action) 
+		{
+			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
+			OnRowDeleting(e);
+		}
+
+		internal void ChangedDataRow (DataRow dr, DataRowAction action) 
+		{
+			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
+			OnRowChanged (e);
+		}
+
+		internal void ChangingDataRow (DataRow dr, DataRowAction action) 
+		{
+			DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
+			OnRowChanging (e);
+		}
+
+		/// <summary>
+		/// Gets the collection of child relations for this DataTable.
+		/// </summary>
+		[Browsable (false)]
+		[DataSysDescription ("Returns the child relations for this table.")]
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataRelationCollection ChildRelations {
+			get {
+				return _childRelations;
+			}
+		}
+
+		/// <summary>
+		/// Gets the collection of columns that belong to this table.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("The collection that holds the columns for this table.")]
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+		public DataColumnCollection Columns {
+			get { return _columnCollection; }
+		}
+
+		/// <summary>
+		/// Gets the collection of constraints maintained by this table.
+		/// </summary>
+		[DataCategory ("Data")]	
+		[DataSysDescription ("The collection that holds the constraints for this table.")]
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+		public ConstraintCollection Constraints {
+			get { return _constraintCollection; }
+		}
+
+		/// <summary>
+		/// Gets the DataSet that this table belongs to.
+		/// </summary>
+		[Browsable (false)]
+		[DataSysDescription ("Indicates the DataSet to which this table belongs.")]
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataSet DataSet {
+			get { return dataSet; }
+		}
+
+		
+
+		/// <summary>
+		/// Gets a customized view of the table which may 
+		/// include a filtered view, or a cursor position.
+		/// </summary>
+		[MonoTODO]	
+		[Browsable (false)]
+		[DataSysDescription ("This is the default DataView for the table.")]
+		public DataView DefaultView {
+			get { return _defaultView; }
+		}
+		
+
+		/// <summary>
+		/// Gets or sets the expression that will return 
+		/// a value used to represent this table in the user interface.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("The expression used to compute the data-bound value of this row.")]	
+		[DefaultValue ("")]
+		public string DisplayExpression {
+			get { return "" + _displayExpression; }
+			set { _displayExpression = value; }
+		}
+
+		/// <summary>
+		/// Gets the collection of customized user information.
+		/// </summary>
+		[Browsable (false)]
+		[DataCategory ("Data")]
+		[DataSysDescription ("The collection that holds custom user information.")]
+		public PropertyCollection ExtendedProperties {
+			get { return _extendedProperties; }
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether there are errors in 
+		/// any of the_rows in any of the tables of the DataSet to 
+		/// which the table belongs.
+		/// </summary>
+		[Browsable (false)]
+		[DataSysDescription ("Returns whether the table has errors.")]
+		public bool HasErrors {
+			get { 
+				// we can not use the _hasError flag because we do not know when to turn it off!
+				for (int i = 0; i < _rows.Count; i++)
+				{
+					if (_rows[i].HasErrors)
+						return true;
+				}
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the locale information used to 
+		/// compare strings within the table.
+		/// </summary>
+		[DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
+		public CultureInfo Locale {
+			get { 
+				// if the locale is null, we check for the DataSet locale
+				// and if the DataSet is null we return the current culture.
+				// this way if DataSet locale is changed, only if there is no locale for 
+				// the DataTable it influece the Locale get;
+				if (_locale != null)
+					return _locale;
+				if (DataSet != null)
+					return DataSet.Locale;
+				return CultureInfo.CurrentCulture;
+			}
+			set { 
+				if (_locale == null || !_locale.Equals(value))
+					_locale = value; 
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the initial starting size for this table.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("Indicates an initial starting size for this table.")]
+		[DefaultValue (50)]
+		public int MinimumCapacity {
+			get { return _minimumCapacity; }
+			set { _minimumCapacity = value; }
+		}
+
+		/// <summary>
+		/// Gets or sets the namespace for the XML represenation 
+		/// of the data stored in the DataTable.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
+		public string Namespace {
+			get { return "" + _nameSpace; }
+			set { _nameSpace = value; }
+		}
+
+		/// <summary>
+		/// Gets the collection of parent relations for 
+		/// this DataTable.
+		/// </summary>
+		[Browsable (false)]
+		[DataSysDescription ("Returns the parent relations for this table.")]
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataRelationCollection ParentRelations {
+			get {	
+				return _parentRelations;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the namespace for the XML represenation
+		///  of the data stored in the DataTable.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
+		[DefaultValue ("")]
+		public string Prefix {
+			get { return "" + _prefix; }
+			set { _prefix = value; }
+		}
+
+		/// <summary>
+		/// Gets or sets an array of columns that function as 
+		/// primary keys for the data table.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
+		public DataColumn[] PrimaryKey {
+			get {
+				UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
+				if (null == uc) return new DataColumn[] {};
+				return uc.Columns;
+			}
+			set {
+
+				//YUK: msft removes a previous unique constraint if it is flagged as a pk  
+				//when a new pk is set 
+				
+				//clear Primary Key if value == null
+				if (null == value) {
+					
+					RemoveUniqueConstraints ();
+					return;
+				}
+
+				//Does constraint exist for these columns
+				UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
+					this.Constraints, (DataColumn[]) value);
+
+				//if constraint doesn't exist for columns
+				//create new unique primary key constraint
+				if (null == uc) {
+
+					RemoveUniqueConstraints ();						
+					
+					foreach (DataColumn Col in (DataColumn[]) value) {
+
+						if (Col.Table == null)
+							break;
+
+						if (Columns.IndexOf (Col) < 0)
+							throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
+					}
+
+
+					uc = new UniqueConstraint( (DataColumn[]) value, true);
+					
+					Constraints.Add (uc);
+				}
+				else { //set existing constraint as the new primary key
+					UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
+				}
+				
+			}
+		}
+
+		/// <summary>
+		/// Gets the collection of_rows that belong to this table.
+		/// </summary>
+		[Browsable (false)]
+		[DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]	
+		public DataRowCollection Rows {
+			get { return _rows; }
+		}
+
+		/// <summary>
+		/// Gets or sets an System.ComponentModel.ISite 
+		/// for the DataTable.
+		/// </summary>
+		[Browsable (false)]
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public override ISite Site {
+			get { return _site; }
+			set { _site = value; }
+		}
+
+		/// <summary>
+		/// Gets or sets the name of the the DataTable.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
+		[DefaultValue ("")]	
+		[RefreshProperties (RefreshProperties.All)]
+		public string TableName {
+			get { return "" + _tableName; }
+			set { _tableName = value; }
+		}
+		
+		bool IListSource.ContainsListCollection {
+			get {
+				// the collection is a DataView
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Commits all the changes made to this table since the 
+		/// last time AcceptChanges was called.
+		/// </summary>
+		public void AcceptChanges () 
+		{
+			//FIXME: Do we need to validate anything here or
+			//try to catch any errors to deal with them?
+			
+			// we do not use foreach because if one of the rows is in Delete state
+			// it will be romeved from Rows and we get an exception.
+			DataRow myRow;
+			for (int i = 0; i < Rows.Count; )
+			{
+				myRow = Rows[i];
+				myRow.AcceptChanges();
+
+				// if the row state is Detached it meens that it was removed from row list (Rows)
+				// so we should not increase 'i'.
+				if (myRow.RowState != DataRowState.Detached)
+					i++;
+			}
+		}
+
+		/// <summary>
+		/// Begins the initialization of a DataTable that is used 
+		/// on a form or used by another component. The initialization
+		/// occurs at runtime.
+		/// </summary>
+		public void BeginInit () 
+		{
+		}
+
+		/// <summary>
+		/// Turns off notifications, index maintenance, and 
+		/// constraints while loading data.
+		/// </summary>
+		[MonoTODO]
+		public void BeginLoadData () 
+		{
+			if (!this._duringDataLoad)
+			{
+				//duringDataLoad is important to EndLoadData and
+				//for not throwing unexpected exceptions.
+				this._duringDataLoad = true;
+			
+				if (this.dataSet != null)
+				{
+					//Saving old Enforce constraints state for later
+					//use in the EndLoadData.
+					this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
+					this.dataSet.EnforceConstraints = false;
+				}
+			}
+			return;
+		}
+
+		/// <summary>
+		/// Clears the DataTable of all data.
+		/// </summary>
+		public void Clear () {
+			// TODO: thow an exception if any rows that 
+			//       have enforced child relations 
+			//       that would result in child rows being orphaned
+			// now we check if any ForeignKeyConstraint is referncing 'table'.
+			if (DataSet != null)
+			{
+				IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
+			
+				// loop on all tables in dataset
+				while (tableEnumerator.MoveNext())
+				{
+					IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
+					// loop on all constrains in the current table
+					while (constraintEnumerator.MoveNext())
+					{
+						Object o = constraintEnumerator.Current;
+						// we only check ForeignKeyConstraint
+						if (o is ForeignKeyConstraint)
+						{
+							ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
+							if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
+								throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
+						}
+					}
+				}
+			}
+			
+			// TODO: throw a NotSupportedException if the DataTable is part
+			//       of a DataSet that is binded to an XmlDataDocument
+			
+			_rows.Clear ();
+		}
+
+		/// <summary>
+		/// Clones the structure of the DataTable, including
+		///  all DataTable schemas and constraints.
+		/// </summary>
+		[MonoTODO]
+		public virtual DataTable Clone () 
+		{
+			DataTable Copy = new DataTable ();			
+			
+			CopyProperties (Copy);
+			return Copy;
+		}
+
+		/// <summary>
+		/// Computes the given expression on the current_rows that 
+		/// pass the filter criteria.
+		/// </summary>
+		[MonoTODO]
+		public object Compute (string expression, string filter) 
+		{
+			// TODO: implement this function based
+			//       on Select (expression)
+			//
+			// expression is an aggregate function
+			// filter is an expression used to limit rows
+
+			object obj = null;
+
+			// filter rows
+			ExpressionElement Expression = new ExpressionMainElement (filter);
+			
+			ArrayList List = new ArrayList ();
+			foreach (DataRow Row in Rows) {
+				
+				if (Expression.Test (Row))
+					List.Add (Row);
+			}
+			
+			DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
+
+			// TODO: with the filtered rows, execute the aggregate function
+			//       mentioned in expression
+
+			return obj;
+		}
+
+		/// <summary>
+		/// Copies both the structure and data for this DataTable.
+		/// </summary>
+		[MonoTODO]	
+		public DataTable Copy () 
+		{
+			DataTable Copy = new DataTable ();
+			CopyProperties (Copy);
+
+			// we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
+			// because if the row state is deleted we get an exception.
+			Copy._duringDataLoad = true;
+			foreach (DataRow Row in Rows) {
+				DataRow NewRow = Copy.NewRow ();
+				Copy.Rows.Add (NewRow);
+				Row.CopyValuesToRow(NewRow);
+			}
+		       	Copy._duringDataLoad = false;		
+			return Copy;
+		}
+
+		[MonoTODO]
+		private void CopyProperties (DataTable Copy) 
+		{
+					
+			Copy.CaseSensitive = CaseSensitive;
+			Copy.VirginCaseSensitive = VirginCaseSensitive;
+
+			// Copy.ChildRelations
+			// Copy.Constraints
+			// Copy.Container
+			// Copy.DefaultView
+			// Copy.DesignMode
+			Copy.DisplayExpression = DisplayExpression;
+			// Copy.ExtendedProperties
+			Copy.Locale = Locale;
+			Copy.MinimumCapacity = MinimumCapacity;
+			Copy.Namespace = Namespace;
+			// Copy.ParentRelations
+			Copy.Prefix = Prefix;
+			Copy.Site = Site;
+			Copy.TableName = TableName;
+
+
+
+			// Copy columns
+			foreach (DataColumn Column in Columns) {
+				
+				Copy.Columns.Add (CopyColumn (Column));	
+			}
+
+			CopyConstraints(Copy);
+			// add primary key to the copy
+			if (PrimaryKey.Length > 0)
+			{
+				DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
+				for (int i = 0; i < pColumns.Length; i++)
+					pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
+
+				Copy.PrimaryKey = pColumns;
+			}
+		}
+
+		private void CopyConstraints(DataTable copy)
+		{
+			UniqueConstraint origUc;
+			UniqueConstraint copyUc;
+			for (int i = 0; i < this.Constraints.Count; i++)
+			{
+				if (this.Constraints[i] is UniqueConstraint)
+				{
+					origUc = (UniqueConstraint)this.Constraints[i];
+					DataColumn[] columns = new DataColumn[origUc.Columns.Length];
+					for (int j = 0; j < columns.Length; j++)
+						columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
+					
+					copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
+					copy.Constraints.Add(copyUc);
+				}
+			}
+		}
+		/// <summary>
+		/// Ends the initialization of a DataTable that is used 
+		/// on a form or used by another component. The 
+		/// initialization occurs at runtime.
+		/// </summary>
+		[MonoTODO]
+		public void EndInit () 
+		{
+
+		}
+
+		/// <summary>
+		/// Turns on notifications, index maintenance, and 
+		/// constraints after loading data.
+		/// </summary>
+		[MonoTODO]
+		public void EndLoadData() 
+		{
+			int i = 0;
+			if (this._duringDataLoad) 
+			{
+				//Returning from loading mode, raising exceptions as usual
+				this._duringDataLoad = false;
+				
+				if (this.dataSet !=null)
+				{
+					//Getting back to previous EnforceConstraint state
+					this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
+				}
+				for (i=0 ; i<this.Rows.Count ; i++)
+				{
+					if (this.Rows[i]._nullConstraintViolation )
+					{
+						throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
+					}
+						
+				}
+
+			}
+
+		}
+
+		/// <summary>
+		/// Gets a copy of the DataTable that contains all
+		///  changes made to it since it was loaded or 
+		///  AcceptChanges was last called.
+		/// </summary>
+		[MonoTODO]
+		public DataTable GetChanges() 
+		{
+			return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
+		}
+
+		/// <summary>
+		/// Gets a copy of the DataTable containing all 
+		/// changes made to it since it was last loaded, or 
+		/// since AcceptChanges was called, filtered by DataRowState.
+		/// </summary>
+		[MonoTODO]	
+		public DataTable GetChanges(DataRowState rowStates) 
+		{
+			DataTable copyTable = null;
+
+			IEnumerator rowEnumerator = Rows.GetEnumerator();
+			while (rowEnumerator.MoveNext()) {
+				DataRow row = (DataRow)rowEnumerator.Current;
+				if (row.IsRowChanged(rowStates)) {
+					if (copyTable == null)
+						copyTable = Clone();
+					DataRow newRow = copyTable.NewRow();
+					copyTable.Rows.Add(newRow);
+					row.CopyValuesToRow(newRow);
+				}
+			}
+			 
+			return copyTable;
+		}
+
+		/// <summary>
+		/// Gets an array of DataRow objects that contain errors.
+		/// </summary>
+		[MonoTODO]
+		public DataRow[] GetErrors () 
+		{
+			ArrayList errors = new ArrayList();
+			for (int i = 0; i < _rows.Count; i++)
+			{
+				if (_rows[i].HasErrors)
+					errors.Add(_rows[i]);
+			}
+			
+			return (DataRow[]) errors.ToArray(typeof(DataRow));
+		}
+	
+		/// <summary>
+		/// This member is only meant to support Mono's infrastructure 
+		/// </summary>
+		protected virtual DataTable CreateInstance () 
+		{
+			return Activator.CreateInstance (this.GetType (), true) as DataTable;
+		}
+
+		/// <summary>
+		/// This member is only meant to support Mono's infrastructure 
+		/// </summary>
+		protected virtual Type GetRowType () 
+		{
+			return typeof (DataRow);
+		}
+
+		/// <summary>
+		/// This member is only meant to support Mono's infrastructure 
+		/// 
+		/// Used for Data Binding between System.Web.UI. controls 
+		/// like a DataGrid
+		/// or
+		/// System.Windows.Forms controls like a DataGrid
+		/// </summary>
+		IList IListSource.GetList () 
+		{
+			IList list = (IList) _defaultView;
+			return list;
+		}
+				
+		/// <summary>
+		/// Copies a DataRow into a DataTable, preserving any 
+		/// property settings, as well as original and current values.
+		/// </summary>
+		[MonoTODO]
+		public void ImportRow (DataRow row) 
+		{
+			DataRow newRow = NewRow();
+			Rows.Add(newRow);
+			row.CopyValuesToRow(newRow);
+			
+		}
+
+		/// <summary>
+		/// This member is only meant to support Mono's infrastructure 		
+		/// </summary>
+		[MonoTODO]
+		void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
+		{
+		}
+
+		/// <summary>
+		/// Finds and updates a specific row. If no matching row
+		///  is found, a new row is created using the given values.
+		/// </summary>
+		[MonoTODO]
+		public DataRow LoadDataRow (object[] values, bool fAcceptChanges) 
+		{
+			DataRow row = null;
+			if (PrimaryKey.Length == 0) {
+				row = Rows.Add (values);
+				if (fAcceptChanges)
+					row.AcceptChanges ();
+			}
+			else {
+				bool hasPrimaryValues = true;
+				// initiate an array that has the values of the primary keys.
+				object[] keyValues = new object[PrimaryKey.Length];
+				for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
+				{
+					if(PrimaryKey[i].Ordinal < values.Length)
+						keyValues[i] = values[PrimaryKey[i].Ordinal];
+					else
+						hasPrimaryValues = false;
+				}
+				
+				if (hasPrimaryValues){
+					// find the row in the table.
+					row = Rows.Find(keyValues);
+				}
+				
+				if (row == null)
+					row = Rows.Add (values);
+				else
+					row.ItemArray = values;
+				
+				if (fAcceptChanges)
+					row.AcceptChanges ();
+			}
+				
+			return row;
+		}
+
+		/// <summary>
+		/// Creates a new DataRow with the same schema as the table.
+		/// </summary>
+		public DataRow NewRow () 
+		{
+			return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
+		}
+
+		/// <summary>
+		/// This member supports the .NET Framework infrastructure
+		///  and is not intended to be used directly from your code.
+		/// </summary>
+		protected internal DataRow[] NewRowArray (int size) 
+		{
+			return (DataRow[]) Array.CreateInstance (GetRowType (), size);
+		}
+
+		/// <summary>
+		/// Creates a new row from an existing row.
+		/// </summary>
+		protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) 
+		{
+			return new DataRow (builder);
+		}
+	
+
+		/// <summary>
+		/// Rolls back all changes that have been made to the 
+		/// table since it was loaded, or the last time AcceptChanges
+		///  was called.
+		/// </summary>
+		[MonoTODO]
+		public void RejectChanges () 
+		{	
+			for (int i = _rows.Count - 1; i >= 0; i--) {
+				DataRow row = _rows [i];
+				if (row.RowState != DataRowState.Unchanged)
+					_rows [i].RejectChanges ();
+			}
+		}
+
+		/// <summary>
+		/// Resets the DataTable to its original state.
+		/// </summary>		
+		[MonoTODO]
+		public virtual void Reset () 
+		{
+			Clear();
+			while (ParentRelations.Count > 0)
+			{
+				if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
+					dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
+			}
+
+			while (ChildRelations.Count > 0)
+			{
+				if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
+					dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
+			}
+			Constraints.Clear();
+			Columns.Clear();
+		}
+
+		/// <summary>
+		/// Gets an array of all DataRow objects.
+		/// </summary>
+		public DataRow[] Select () 
+		{
+			return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
+		}
+
+		/// <summary>
+		/// Gets an array of all DataRow objects that match 
+		/// the filter criteria in order of primary key (or 
+		/// lacking one, order of addition.)
+		/// </summary>
+		public DataRow[] Select (string filterExpression) 
+		{
+			return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
+		}
+
+		/// <summary>
+		/// Gets an array of all DataRow objects that 
+		/// match the filter criteria, in the the 
+		/// specified sort order.
+		/// </summary>
+		public DataRow[] Select (string filterExpression, string sort) 
+		{
+			return Select(filterExpression, sort, DataViewRowState.CurrentRows);
+		}
+
+		/// <summary>
+		/// Gets an array of all DataRow objects that match
+		/// the filter in the order of the sort, that match 
+		/// the specified state.
+		/// </summary>
+		[MonoTODO]
+		public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) 
+		{
+			DataRow[] dataRows = null;
+			if (filterExpression == null)
+				filterExpression = String.Empty;
+
+			ExpressionElement Expression = null;
+			if (filterExpression != null && filterExpression.Length != 0)
+				Expression = new ExpressionMainElement(filterExpression);
+
+			ArrayList List = new ArrayList();
+			int recordStateFilter = GetRowStateFilter(recordStates);
+			foreach (DataRow Row in Rows) 
+			{
+				if (((int)Row.RowState & recordStateFilter) != 0)
+				{
+					if (Expression == null || Expression.Test(Row))
+						List.Add(Row);
+				}
+			}
+
+			dataRows = (DataRow[])List.ToArray(typeof(DataRow));
+			
+
+			if (sort != null && !sort.Equals(String.Empty)) 
+			{
+				SortableColumn[] sortableColumns = null;
+
+				sortableColumns = ParseTheSortString (sort);
+				if (sortableColumns == null)
+					throw new Exception ("sort expression result is null");
+				if (sortableColumns.Length == 0)
+					throw new Exception("sort expression result is 0");
+
+				RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
+				dataRows = rowSorter.SortRows ();
+
+				sortableColumns = null;
+				rowSorter = null;
+			}
+
+			
+			return dataRows;
+		}
+
+		private static int GetRowStateFilter(DataViewRowState recordStates)
+		{
+			int flag = 0;
+
+			if ((recordStates & DataViewRowState.Added) != 0)
+				flag |= (int)DataRowState.Added;
+			if ((recordStates & DataViewRowState.Deleted) != 0)
+				flag |= (int)DataRowState.Deleted;
+			if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
+				flag |= (int)DataRowState.Modified;
+			if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
+				flag |= (int)DataRowState.Modified;
+			if ((recordStates & DataViewRowState.Unchanged) != 0)
+				flag |= (int)DataRowState.Unchanged;
+
+			return flag;
+		}
+
+		/// <summary>
+		/// Gets the TableName and DisplayExpression, if 
+		/// there is one as a concatenated string.
+		/// </summary>
+		public override string ToString() 
+		{
+			//LAMESPEC: spec says concat the two. impl puts a 
+			//plus sign infront of DisplayExpression
+			string retVal = TableName;
+			if(DisplayExpression != null && DisplayExpression != "")
+				retVal += " + " + DisplayExpression;
+			return retVal;
+		}
+
+		
+		#region Events /////////////////
+		
+		/// <summary>
+		/// Raises the ColumnChanged event.
+		/// </summary>
+		protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
+			if (null != ColumnChanged) {
+				ColumnChanged (this, e);
+			}
+		}
+
+		/// <summary>
+		/// Raises the ColumnChanging event.
+		/// </summary>
+		protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
+			if (null != ColumnChanging) {
+				ColumnChanging (this, e);
+			}
+		}
+
+		/// <summary>
+		/// Raises the PropertyChanging event.
+		/// </summary>
+		[MonoTODO]
+		protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
+			//	if (null != PropertyChanging)
+			//	{
+			//		PropertyChanging (this, e);
+			//	}
+		}
+
+		/// <summary>
+		/// Notifies the DataTable that a DataColumn is being removed.
+		/// </summary>
+		[MonoTODO]
+		protected internal virtual void OnRemoveColumn (DataColumn column) {
+			//	if (null != RemoveColumn)
+			//	{
+			//		RemoveColumn(this, e);
+			//	}
+		}
+
+		/// <summary>
+		/// Raises the RowChanged event.
+		/// </summary>
+		protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
+			if (null != RowChanged) {
+				RowChanged(this, e);
+			}
+		}
+
+
+		/// <summary>
+		/// Raises the RowChanging event.
+		/// </summary>
+		protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
+			if (null != RowChanging) {
+				RowChanging(this, e);
+			}
+		}
+
+		/// <summary>
+		/// Raises the RowDeleted event.
+		/// </summary>
+		protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
+			if (null != RowDeleted) {
+				RowDeleted(this, e);
+			}
+		}
+
+		/// <summary>
+		/// Raises the RowDeleting event.
+		/// </summary>
+		protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
+			if (null != RowDeleting) {
+				RowDeleting(this, e);
+			}
+		}
+
+		[MonoTODO]
+		private DataColumn CopyColumn (DataColumn Column) {
+			DataColumn Copy = new DataColumn ();
+
+			// Copy all the properties of column
+			Copy.AllowDBNull = Column.AllowDBNull;
+			Copy.AutoIncrement = Column.AutoIncrement;
+			Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
+			Copy.AutoIncrementStep = Column.AutoIncrementStep;
+			Copy.Caption = Column.Caption;
+			Copy.ColumnMapping = Column.ColumnMapping;
+			Copy.ColumnName = Column.ColumnName;
+			//Copy.Container
+			Copy.DataType = Column.DataType;
+			Copy.DefaultValue = Column.DefaultValue;			
+			Copy.Expression = Column.Expression;
+			//Copy.ExtendedProperties
+			Copy.MaxLength = Column.MaxLength;
+			Copy.Namespace = Column.Namespace;
+			Copy.Prefix = Column.Prefix;
+			Copy.ReadOnly = Column.ReadOnly;
+			//Copy.Site
+			//we do not copy the unique value - it will be copyied when copying the constraints.
+			//Copy.Unique = Column.Unique;
+			
+			return Copy;
+		}			
+
+		/// <summary>
+		/// Occurs when after a value has been changed for 
+		/// the specified DataColumn in a DataRow.
+		/// </summary>
+		[DataCategory ("Data")]	
+		[DataSysDescription ("Occurs when a value has been changed for this column.")]
+		public event DataColumnChangeEventHandler ColumnChanged;
+
+		/// <summary>
+		/// Occurs when a value is being changed for the specified 
+		/// DataColumn in a DataRow.
+		/// </summary>
+		[DataCategory ("Data")]
+		[DataSysDescription ("Occurs when a value has been submitted for this column. The user can modify the proposed value and should throw an exception to cancel the edit.")]
+		public event DataColumnChangeEventHandler ColumnChanging;
+
+		/// <summary>
+		/// Occurs after a DataRow has been changed successfully.
+		/// </summary>
+		[DataCategory ("Data")]	
+		[DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
+		public event DataRowChangeEventHandler RowChanged;
+
+		/// <summary>
+		/// Occurs when a DataRow is changing.
+		/// </summary>
+		[DataCategory ("Data")]	
+		[DataSysDescription ("Occurs when the row is being changed so that the event handler can modify or cancel the change. The user can modify values in the row and should throw an  exception to cancel the edit.")]
+		public event DataRowChangeEventHandler RowChanging;
+
+		/// <summary>
+		/// Occurs after a row in the table has been deleted.
+		/// </summary>
+		[DataCategory ("Data")]	
+		[DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] 
+		public event DataRowChangeEventHandler RowDeleted;
+
+		/// <summary>
+		/// Occurs before a row in the table is about to be deleted.
+		/// </summary>
+		[DataCategory ("Data")]	
+		[DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
+		public event DataRowChangeEventHandler RowDeleting;
+		
+		#endregion // Events
+
+		/// <summary>
+		///  Removes all UniqueConstraints
+		/// </summary>
+		private void RemoveUniqueConstraints () 
+		{
+			foreach (Constraint Cons in Constraints) {
+				
+				if (Cons is UniqueConstraint) {
+					Constraints.Remove (Cons);
+					break;
+				}
+			}
+			
+			UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
+		}
+
+		// to parse the sort string for DataTable:Select(expression,sort)
+		// into sortable columns (think ORDER BY, 
+		// such as, "customer ASC, price DESC" )
+		private SortableColumn[] ParseTheSortString (string sort) 
+		{
+			SortableColumn[] sortColumns = null;
+			ArrayList columns = null;
+		
+			if (sort != null && !sort.Equals ("")) {
+				columns = new ArrayList ();
+				string[] columnExpression = sort.Trim ().Split (new char[1] {','});
+			
+				for (int c = 0; c < columnExpression.Length; c++) {
+					string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
+				
+					string columnName = columnSortInfo[0].Trim ();
+					string sortOrder = "ASC";
+					if (columnSortInfo.Length > 1) 
+						sortOrder = columnSortInfo[1].Trim ().ToUpper ();
+					
+					ListSortDirection sortDirection = ListSortDirection.Ascending;
+					switch (sortOrder) {
+					case "ASC":
+						sortDirection = ListSortDirection.Ascending;
+						break;
+					case "DESC":
+						sortDirection = ListSortDirection.Descending;
+						break;
+					default:
+						throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
+					}
+					Int32 ord = 0;
+					try {
+						ord = Int32.Parse (columnName);
+					}
+					catch (FormatException) {
+						ord = -1;
+					}
+					DataColumn dc = null;
+					if (ord == -1)				
+						dc = _columnCollection[columnName];
+					else
+						dc = _columnCollection[ord];
+					SortableColumn sortCol = new SortableColumn (dc,sortDirection);
+					columns.Add (sortCol);
+				}	
+				sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
+			}		
+			return sortColumns;
+		}
+	
+		private class SortableColumn 
+		{
+			private DataColumn col;
+			private ListSortDirection dir;
+
+			internal SortableColumn (DataColumn column, 
+						ListSortDirection direction) 
+			{
+				col = column;
+				dir = direction;
+			}
+
+			public DataColumn Column {
+				get {
+					return col;
+				}
+			}
+
+			public ListSortDirection SortDirection {
+				get {
+					return dir;
+				}
+			}
+		}
+
+		private class RowSorter : IComparer 
+		{
+			private SortableColumn[] sortColumns;
+			private DataRow[] rowsToSort;
+			
+			internal RowSorter(DataRow[] unsortedRows, 
+					SortableColumn[] sortColumns) 
+			{
+				this.sortColumns = sortColumns;
+				this.rowsToSort = unsortedRows;
+			}
+
+			public SortableColumn[] SortColumns {
+				get {
+					return sortColumns;
+				}
+			}
+			
+			public DataRow[] SortRows () 
+			{
+				Array.Sort (rowsToSort, this);
+				return rowsToSort;
+			}
+
+			int IComparer.Compare (object x, object y) 
+			{
+				if(x == null)
+					throw new Exception ("Object to compare is null: x");
+				if(y == null)
+					throw new Exception ("Object to compare is null: y");
+				if(!(x is DataRow))
+					throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
+				if(!(y is DataRow))
+					throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
+
+				DataRow rowx = (DataRow) x;
+				DataRow rowy = (DataRow) y;
+
+				for(int i = 0; i < sortColumns.Length; i++) {
+					SortableColumn sortColumn = sortColumns[i];
+					DataColumn dc = sortColumn.Column;
+
+					IComparable objx = (IComparable) rowx[dc];
+					object objy = rowy[dc];
+
+					int result = CompareObjects (objx, objy);
+					if (result != 0) {
+						if (sortColumn.SortDirection == ListSortDirection.Ascending) {
+							return result;
+						}
+						else {
+							return -result;
+						}
+					}
+				}
+				return 0;
+			}
+
+			private int CompareObjects (object a, object b) 
+			{
+				if (a == b)
+					return 0;
+				else if (a == null)
+					return -1;
+				else if (a == DBNull.Value)
+					return -1;
+				else if (b == null)
+					return 1;
+				else if (b == DBNull.Value)
+					return 1;
+
+				if((a is string) && (b is string)) {
+					a = ((string) a).ToUpper ();
+					b = ((string) b).ToUpper ();			
+				}
+
+				if (a is IComparable)
+					return ((a as IComparable).CompareTo (b));
+				else if (b is IComparable)
+					return -((b as IComparable).CompareTo (a));
+
+				throw new ArgumentException ("Neither a nor b IComparable");
+			}
+		}
+	}
+}

+ 39 - 18
mcs/class/System.Data/System.Data/ForeignKeyConstraint.cs

@@ -23,9 +23,9 @@ namespace System.Data {
 		private UniqueConstraint _parentUniqueConstraint;
 		private DataColumn [] _parentColumns;
 		private DataColumn [] _childColumns;
-		private Rule _deleteRule;
-		private Rule _updateRule;
-		private AcceptRejectRule _acceptRejectRule;
+		private Rule _deleteRule = Rule.Cascade;
+		private Rule _updateRule = Rule.Cascade;
+		private AcceptRejectRule _acceptRejectRule = AcceptRejectRule.None;
 		
 		#region Constructors
 
@@ -383,28 +383,49 @@ namespace System.Data {
 		[MonoTODO]
 		internal override void AssertConstraint(DataRow row)
 		{
-			//Implement: this should be used to validate ForeignKeys constraints 
-			//when modifiying the DataRow values of a DataTable.
+			// first we check if all values in _childColumns place are DBNull.
+			// if yes we return.
+			bool allNull = true;
+			for (int i = 0; i < _childColumns.Length; i++)
+			{
+				if (row[_childColumns[i]] != DBNull.Value)
+				{
+					allNull = false;
+					break;
+				}
+			}
 
+			if (allNull)
+				return;
+			
+			// check that there is a parent for this row.
 			foreach (DataRow parentRow in this.RelatedTable.Rows)
 			{
 				if (parentRow.RowState != DataRowState.Deleted)
 				{
-					bool match = true;
-					// check if the values in the constraints columns are equal
-					int i = 0;
-					for (; i < _parentColumns.Length; i++)
-					{
-						if (!row[_childColumns[i]].Equals(parentRow[_parentColumns[i]]))
-						{
-							match = false;
-							break;
-						}	
-					}
-					if (!match)
-						throw new InvalidConstraintException("ForeignKeyConstraint " + ConstraintName + " requires the child key values (" + row[_childColumns[i]].ToString() + ") to exist in the parent table.");					
+					bool match = true;
+					// check if the values in the constraints columns are equal
+					for (int i = 0; i < _parentColumns.Length; i++)
+					{
+						if (!row[_childColumns[i]].Equals(parentRow[_parentColumns[i]]))
+						{
+							match = false;
+							break;
+						}	
+					}
+					if (match) // there is a parent row for this row.
+						return;
 				}
 			}
+			
+			string values = "";
+			for (int i = 0; i < _childColumns.Length; i++)
+			{
+				values += row[_childColumns[0]].ToString();
+				if (i != _childColumns.Length - 1)
+					values += ",";
+			}
+			throw new InvalidConstraintException("ForeignKeyConstraint " + ConstraintName + " requires the child key values (" + values + ") to exist in the parent table.");
 		}
 		
 		#endregion // Methods

+ 223 - 215
mcs/class/System.Data/System.Data/MergeManager.cs

@@ -1,215 +1,223 @@
-using System;
-
-namespace System.Data
-{
-	/// <summary>
-	/// Summary description for MergeManager.
-	/// </summary>
-	internal class MergeManager
-	{
-		internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
-		{
-			if(targetSet == null)
-				throw new ArgumentNullException("targetSet");
-			if(sourceSet == null)
-				throw new ArgumentNullException("sourceSet");
-
-			foreach (DataTable t in sourceSet.Tables)
-				MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);
-
-		}
-
-		internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)
-		{
-			if(targetSet == null)
-				throw new ArgumentNullException("targetSet");
-			if(sourceTable == null)
-				throw new ArgumentNullException("sourceTable");
-
-			
-			if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))
-				return;
-			checkColumnTypes(targetSet.Tables[sourceTable.TableName], sourceTable); // check that the colums datatype is the same
-			fillData(targetSet.Tables[sourceTable.TableName], sourceTable, preserveChanges);
-			
-		}
-
-		internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
-		{
-			if(targetSet == null)
-				throw new ArgumentNullException("targetSet");
-			if(sourceRows == null)
-				throw new ArgumentNullException("sourceRows");
-
-			for (int i = 0; i < sourceRows.Length; i++)
-			{
-				DataRow row = sourceRows[i];
-				DataTable sourceTable = row.Table;
-				if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))
-					return;
-				checkColumnTypes(targetSet.Tables[row.Table.TableName], row.Table);
-				MergeRow(targetSet.Tables[sourceTable.TableName], row, preserveChanges);
-			}
-		}
-
-		// merge a row into a target table.
-		private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)
-		{
-			DataColumnCollection columns = row.Table.Columns;
-			DataColumn[] primaryKeys = targetTable.PrimaryKey;
-			DataRow targetRow = null;
-			DataRowVersion version = DataRowVersion.Default;
-			if (row.RowState == DataRowState.Deleted)
-				version = DataRowVersion.Original;
-
-			if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key.
-			{
-				// initiate an array that has the values of the primary keys.
-				object[] keyValues = new object[primaryKeys.Length];
-				
-				for (int j = 0; j < keyValues.Length; j++)
-				{
-					keyValues[j] = row[primaryKeys[j].ColumnName, version];
-				}
-			
-				// find the row in the target table.
-				targetRow = targetTable.Rows.Find(keyValues);
-			}
-			// row doesn't exist in target table, or there are no primary keys.
-			// create new row and copy values from source row to the new row.
-			if (targetRow == null)
-			{ 
-				targetRow = targetTable.NewRow();
-				row.CopyValuesToRow(targetRow);
-				targetTable.Rows.Add(targetRow);
-			}
-			// row exists in target table, and presere changes is false - 
-			// change the values of the target row to the values of the source row.
-			else if (!preserveChanges)
-			{
-				row.CopyValuesToRow(targetRow);
-			}
-
-		}
-			
-		
-		
-		// adjust the table schema according to the missingschemaaction param.
-		// return false if adjusting fails.
-		private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction)
-		{
-			string tableName = sourceTable.TableName;
-			
-			// if the source table not exists in the target dataset
-			// we act according to the missingschemaaction param.
-			if (!targetSet.Tables.Contains(tableName))
-			{
-				if (missingSchemaAction == MissingSchemaAction.Ignore)
-					return true;
-				if (missingSchemaAction == MissingSchemaAction.Error)
-					throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");
-				targetSet.Tables.Add((DataTable)sourceTable.Clone());
-			}
-			
-			DataTable table = targetSet.Tables[tableName];
-			
-			if (!CheckPrimaryKeys(table, sourceTable))
-				return false;
-			
-			for (int i = 0; i < sourceTable.Columns.Count; i++)
-			{
-				DataColumn col = sourceTable.Columns[i];
-				// if a column from the source table doesn't exists in the target table
-				// we act according to the missingschemaaction param.
-				if(!table.Columns.Contains(col.ColumnName))
-				{
-					if (missingSchemaAction == MissingSchemaAction.Ignore)
-						continue;
-					if (missingSchemaAction == MissingSchemaAction.Error)
-						throw new ArgumentException(("Column '" + col.ColumnName + "' does not belong to table Items."));
-					
-					table.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));
-				}
-			}
-
-			return true;
-		}
-		
-		// find if there is a valid matching between the targetTable PrimaryKey and the
-		// sourceTable primatyKey.
-		// return true if there is a match, else return false and raise a MergeFailedEvent.
-		private static bool CheckPrimaryKeys(DataTable targetTable, DataTable sourceTable)
-		{
-			// if the length of one of the tables primarykey if 0 - there is nothing to check.
-			if (targetTable.PrimaryKey.Length != 0 && sourceTable.PrimaryKey.Length != 0)
-			{
-				// if the length of primarykey is not equal - merge fails
-				if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length)
-				{
-					string message = "<target>.PrimaryKey and <source>.PrimaryKey have different Length.";
-					MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
-					targetTable.DataSet.OnMergeFailed(e);
-					return false;
-				}
-				else
-				{
-					// we have to see that each primary column in the target table
-					// has a column with the same name in the sourcetable primarykey columns. 
-					bool foundMatch;
-					DataColumn[] targetDataColumns = targetTable.PrimaryKey;
-					DataColumn[] srcDataColumns = sourceTable.PrimaryKey;
-
-					// loop on all primary key columns in the targetTable.
-					for (int i = 0; i < targetDataColumns.Length; i++)
-					{
-						foundMatch = false;
-						DataColumn col = targetDataColumns[i];
-
-						// find if there is a column with the same name in the 
-						// sourceTable primary key columns.
-						for (int j = 0; j < srcDataColumns.Length; j++)
-						{
-							if (srcDataColumns[j].ColumnName == col.ColumnName)
-							{
-								foundMatch = true;
-								break;
-							}
-						}
-						if (!foundMatch)
-						{
-							string message = "Mismatch columns in the PrimaryKey : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";
-							MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
-							targetTable.DataSet.OnMergeFailed(e);
-							return false;
-						}
-						
-					}
-				}
-			}
-
-			return true;
-		}
-		
-		// fill the data from the source table to the target table
-		private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)
-		{
-			for (int i = 0; i < sourceTable.Rows.Count; i++)
-			{
-				DataRow row = sourceTable.Rows[i];
-				MergeRow(targetTable, row, preserveChanges);
-			}
-		}
-		
-		// check tha column from 2 tables that has the same name also has the same datatype.
-		private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)
-		{
-			for (int i = 0; i < sourceTable.Columns.Count; i++)
-			{
-				DataColumn fromCol = sourceTable.Columns[i];
-				DataColumn toCol = targetTable.Columns[fromCol.ColumnName];
-				if(toCol.DataType != fromCol.DataType)
-					throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");
-			}
-		}
-	}
-}
+using System;
+
+namespace System.Data
+{
+	/// <summary>
+	/// Summary description for MergeManager.
+	/// </summary>
+	internal class MergeManager
+	{
+		internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
+		{
+			if(targetSet == null)
+				throw new ArgumentNullException("targetSet");
+			if(sourceSet == null)
+				throw new ArgumentNullException("sourceSet");
+
+			foreach (DataTable t in sourceSet.Tables)
+				MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);
+
+		}
+
+		internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)
+		{
+			if(targetSet == null)
+				throw new ArgumentNullException("targetSet");
+			if(sourceTable == null)
+				throw new ArgumentNullException("sourceTable");
+
+			
+			if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))
+				return;
+			DataTable targetTable = targetSet.Tables[sourceTable.TableName];
+			if (targetTable != null)
+			{
+				checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same
+				fillData(targetTable, sourceTable, preserveChanges);
+			}
+			
+		}
+
+		internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
+		{
+			if(targetSet == null)
+				throw new ArgumentNullException("targetSet");
+			if(sourceRows == null)
+				throw new ArgumentNullException("sourceRows");
+
+			for (int i = 0; i < sourceRows.Length; i++)
+			{
+				DataRow row = sourceRows[i];
+				DataTable sourceTable = row.Table;
+				if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))
+					return;
+				DataTable targetTable = targetSet.Tables[row.Table.TableName];
+				if (targetTable != null)
+				{
+					checkColumnTypes(targetTable, row.Table);
+					MergeRow(targetTable, row, preserveChanges);
+				}
+			}
+		}
+
+		// merge a row into a target table.
+		private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)
+		{
+			DataColumnCollection columns = row.Table.Columns;
+			DataColumn[] primaryKeys = targetTable.PrimaryKey;
+			DataRow targetRow = null;
+			DataRowVersion version = DataRowVersion.Default;
+			if (row.RowState == DataRowState.Deleted)
+				version = DataRowVersion.Original;
+
+			if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key.
+			{
+				// initiate an array that has the values of the primary keys.
+				object[] keyValues = new object[primaryKeys.Length];
+				
+				for (int j = 0; j < keyValues.Length; j++)
+				{
+					keyValues[j] = row[primaryKeys[j].ColumnName, version];
+				}
+			
+				// find the row in the target table.
+				targetRow = targetTable.Rows.Find(keyValues);
+			}
+			// row doesn't exist in target table, or there are no primary keys.
+			// create new row and copy values from source row to the new row.
+			if (targetRow == null)
+			{ 
+				targetRow = targetTable.NewRow();
+				row.CopyValuesToRow(targetRow);
+				targetTable.Rows.Add(targetRow);
+			}
+			// row exists in target table, and presere changes is false - 
+			// change the values of the target row to the values of the source row.
+			else if (!preserveChanges)
+			{
+				row.CopyValuesToRow(targetRow);
+			}
+
+		}
+			
+		
+		
+		// adjust the table schema according to the missingschemaaction param.
+		// return false if adjusting fails.
+		private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction)
+		{
+			string tableName = sourceTable.TableName;
+			
+			// if the source table not exists in the target dataset
+			// we act according to the missingschemaaction param.
+			if (!targetSet.Tables.Contains(tableName))
+			{
+				if (missingSchemaAction == MissingSchemaAction.Ignore)
+					return true;
+				if (missingSchemaAction == MissingSchemaAction.Error)
+					throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");
+				targetSet.Tables.Add((DataTable)sourceTable.Clone());
+			}
+			
+			DataTable table = targetSet.Tables[tableName];
+			
+			if (!CheckPrimaryKeys(table, sourceTable))
+				return false;
+			
+			for (int i = 0; i < sourceTable.Columns.Count; i++)
+			{
+				DataColumn col = sourceTable.Columns[i];
+				// if a column from the source table doesn't exists in the target table
+				// we act according to the missingschemaaction param.
+				if(!table.Columns.Contains(col.ColumnName))
+				{
+					if (missingSchemaAction == MissingSchemaAction.Ignore)
+						continue;
+					if (missingSchemaAction == MissingSchemaAction.Error)
+						throw new ArgumentException(("Column '" + col.ColumnName + "' does not belong to table Items."));
+					
+					table.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));
+				}
+			}
+
+			return true;
+		}
+		
+		// find if there is a valid matching between the targetTable PrimaryKey and the
+		// sourceTable primatyKey.
+		// return true if there is a match, else return false and raise a MergeFailedEvent.
+		private static bool CheckPrimaryKeys(DataTable targetTable, DataTable sourceTable)
+		{
+			// if the length of one of the tables primarykey if 0 - there is nothing to check.
+			if (targetTable.PrimaryKey.Length != 0 && sourceTable.PrimaryKey.Length != 0)
+			{
+				// if the length of primarykey is not equal - merge fails
+				if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length)
+				{
+					string message = "<target>.PrimaryKey and <source>.PrimaryKey have different Length.";
+					MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
+					targetTable.DataSet.OnMergeFailed(e);
+					return false;
+				}
+				else
+				{
+					// we have to see that each primary column in the target table
+					// has a column with the same name in the sourcetable primarykey columns. 
+					bool foundMatch;
+					DataColumn[] targetDataColumns = targetTable.PrimaryKey;
+					DataColumn[] srcDataColumns = sourceTable.PrimaryKey;
+
+					// loop on all primary key columns in the targetTable.
+					for (int i = 0; i < targetDataColumns.Length; i++)
+					{
+						foundMatch = false;
+						DataColumn col = targetDataColumns[i];
+
+						// find if there is a column with the same name in the 
+						// sourceTable primary key columns.
+						for (int j = 0; j < srcDataColumns.Length; j++)
+						{
+							if (srcDataColumns[j].ColumnName == col.ColumnName)
+							{
+								foundMatch = true;
+								break;
+							}
+						}
+						if (!foundMatch)
+						{
+							string message = "Mismatch columns in the PrimaryKey : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";
+							MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
+							targetTable.DataSet.OnMergeFailed(e);
+							return false;
+						}
+						
+					}
+				}
+			}
+
+			return true;
+		}
+		
+		// fill the data from the source table to the target table
+		private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)
+		{
+			for (int i = 0; i < sourceTable.Rows.Count; i++)
+			{
+				DataRow row = sourceTable.Rows[i];
+				MergeRow(targetTable, row, preserveChanges);
+			}
+		}
+		
+		// check tha column from 2 tables that has the same name also has the same datatype.
+		private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)
+		{
+			for (int i = 0; i < sourceTable.Columns.Count; i++)
+			{
+				DataColumn fromCol = sourceTable.Columns[i];
+				DataColumn toCol = targetTable.Columns[fromCol.ColumnName];
+				if((toCol != null) && (toCol.DataType != fromCol.DataType))
+					throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");
+			}
+		}
+	}
+}

+ 1 - 1
mcs/class/System.Data/System.Data/UniqueConstraint.cs

@@ -385,7 +385,7 @@ namespace System.Data {
 						}	
 					}
 					if (match)
-						throw new InvalidConstraintException (String.Format ("Column '{0}' contains non-unique values", this._dataColumns[0]));					
+						throw new ConstraintException (String.Format ("Column '{0}' contains non-unique values", this._dataColumns[0]));					
 				}
 			}