2004-05-14 Umadevi S <sumadevi@novell.com>
[mono.git] / mcs / class / System.Data / System.Data / DataRow.cs
index d1f24c886ead1fbf499d0e8757b5f39a5cc51f5d..fe0869907486b8a9370bb3edf6d71b8e206099e8 100644 (file)
@@ -5,14 +5,18 @@
 //   Rodrigo Moya <rodrigo@ximian.com>
 //   Daniel Morgan <danmorg@sc.rr.com>
 //   Tim Coleman <tim@timcoleman.com>
+//   Ville Palo <vi64pa@koti.soon.fi>
+//   Alan Tam Siu Lung <Tam@SiuLung.com>
 //
 // (C) Ximian, Inc 2002
-// (C) Daniel Morgan 2002
+// (C) Daniel Morgan 2002, 2003
 // Copyright (C) 2002 Tim Coleman
 //
 
 using System;
 using System.Collections;
+using System.Globalization;
+using System.Xml;
 
 namespace System.Data {
        /// <summary>
@@ -32,6 +36,16 @@ namespace System.Data {
                private string[] columnErrors;
                private string rowError;
                private DataRowState rowState;
+               internal int xmlRowID = 0;
+               internal bool _nullConstraintViolation;
+               private string _nullConstraintMessage;
+               private bool editing = false;
+               private bool _hasParentCollection;
+               private bool _inChangingEvent;
+               private int _rowId;
+               internal bool _inExpressionEvaluation = false;
+
+               private XmlDataDocument.XmlDataElement mappedElement;
 
                #endregion
 
@@ -44,20 +58,41 @@ namespace System.Data {
                protected internal DataRow (DataRowBuilder builder)
                {
                        _table = builder.Table;
+                       // Get the row id from the builder.
+                       _rowId = builder._rowId;
 
                        original = null; 
-                       proposed = null;
-                       current = new object[_table.Columns.Count];
-
+                       
+                       proposed = new object[_table.Columns.Count];
+                       // Initialise the data coloumns of the row with the dafault values, if any 
+                       for (int c = 0; c < _table.Columns.Count; c++) 
+                       {
+                               if(_table.Columns [c].DefaultValue == null)
+                                       proposed[c] = DBNull.Value;
+                               else
+                                       proposed [c] = _table.Columns[c].DefaultValue;
+                       }
+                       
                        columnErrors = new string[_table.Columns.Count];
                        rowError = String.Empty;
 
-                       //rowState = DataRowState.Unchanged;
-
                        //on first creating a DataRow it is always detached.
                        rowState = DataRowState.Detached;
+                       
+                       ArrayList aiColumns = _table.Columns.AutoIncrmentColumns;
+                       foreach (string col in aiColumns) {
+                               DataColumn dc = _table.Columns[col];
+                               this [dc] = dc.AutoIncrementValue();
+                       }
+                       _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
+
+                       // create mapped XmlDataElement
+                       DataSet ds = _table.DataSet;
+                       if (ds != null && ds._xmlDataDocument != null)
+                               mappedElement = new XmlDataDocument.XmlDataElement (this, _table.Prefix, _table.TableName, _table.Namespace, ds._xmlDataDocument);
                }
 
+               
                #endregion
 
                #region Properties
@@ -66,9 +101,16 @@ namespace System.Data {
                /// Gets a value indicating whether there are errors in a row.
                /// </summary>
                public bool HasErrors {
-                       [MonoTODO]
                        get {
-                               throw new NotImplementedException ();
+                               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;
                        }
                }
 
@@ -76,14 +118,12 @@ namespace System.Data {
                /// Gets or sets the data stored in the column specified by name.
                /// </summary>
                public object this[string columnName] {
-                       [MonoTODO] //FIXME: will return different values depending on DataRowState
-                       get { return this[columnName, DataRowVersion.Current]; }
-                       [MonoTODO]
+                       get { return this[columnName, DataRowVersion.Default]; }
                        set {
-                               DataColumn column = _table.Columns[columnName];
-                               if (column == null) 
+                               int columnIndex = _table.Columns.IndexOf (columnName);
+                               if (columnIndex == -1)
                                        throw new IndexOutOfRangeException ();
-                               this[column] = value;
+                               this[columnIndex] = value;
                        }
                }
 
@@ -91,54 +131,14 @@ namespace System.Data {
                /// Gets or sets the data stored in specified DataColumn
                /// </summary>
                public object this[DataColumn column] {
-                       [MonoTODO] //FIXME: will return different values depending on DataRowState
-                       get { return this[column, DataRowVersion.Current]; } 
-                                                               
-                       [MonoTODO]
+
+                       get {
+                               return this[column, DataRowVersion.Default];} 
                        set {
-                               bool objIsDBNull = value.Equals(DBNull.Value);
-                               if (column == null)
-                                       throw new ArgumentNullException ();
                                int columnIndex = _table.Columns.IndexOf (column);
                                if (columnIndex == -1)
-                                       throw new ArgumentException ();
-                               if(column.DataType != value.GetType ()) {
-                                       if(objIsDBNull == true && column.AllowDBNull == false)
-                                               throw new InvalidCastException ();
-                                       //else if(objIsDBNull == false)
-                                       //      throw new InvalidCastException ();
-                               }
-
-                               if (rowState == DataRowState.Deleted)
-                                       throw new DeletedRowInaccessibleException ();
-
-                               //MS Implementation doesn't seem to create the proposed or original
-                               //set of values when a datarow has just been created or added to the
-                               //DataTable and AcceptChanges() has not been called yet.
-
-                               if(rowState == DataRowState.Detached || rowState == DataRowState.Added) {
-                                       if(objIsDBNull)
-                                               current[columnIndex] = DBNull.Value;
-                                       else
-                                               current[columnIndex] = value;
-                               }
-                               else {
-                                       BeginEdit ();  // implicitly called
-
-                                       rowState = DataRowState.Modified;
-
-                                       if(objIsDBNull)
-                                               proposed[columnIndex] = DBNull.Value;
-                                       else
-                                               proposed[columnIndex] = value;
-                               }
-
-                               //Don't know if this is the rigth thing to do,
-                               //but it fixes my test. I believe the MS docs only say this
-                               //method is implicitly called when calling AcceptChanges()
-
-                               // EndEdit (); // is this the right thing to do?
-
+                                       throw new ArgumentException ("The column does not belong to this table.");
+                               this[columnIndex] = value;
                        }
                }
 
@@ -146,14 +146,22 @@ namespace System.Data {
                /// Gets or sets the data stored in column specified by index.
                /// </summary>
                public object this[int columnIndex] {
-                       [MonoTODO] //FIXME: not always supposed to return current
-                       get { return this[columnIndex, DataRowVersion.Current]; }
-                       [MonoTODO]
+                       get { return this[columnIndex, DataRowVersion.Default]; }
                        set {
-                               DataColumn column = _table.Columns[columnIndex]; //FIXME: will throw
-                               if (column == null)  
+                               if (columnIndex < 0 || columnIndex > _table.Columns.Count)
                                        throw new IndexOutOfRangeException ();
-                               this[column] = value;
+                               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, column, columnIndex);
+                               proposed[columnIndex] = v;
+                               _table.ChangedDataColumn (this, column, v);
+                               if (!orginalEditing) EndEdit ();
                        }
                }
 
@@ -161,12 +169,11 @@ namespace System.Data {
                /// Gets the specified version of data stored in the named column.
                /// </summary>
                public object this[string columnName, DataRowVersion version] {
-                       [MonoTODO]
                        get {
-                               DataColumn column = _table.Columns[columnName]; //FIXME: will throw
-                               if (column == null) 
+                               int columnIndex = _table.Columns.IndexOf (columnName);
+                               if (columnIndex == -1)
                                        throw new IndexOutOfRangeException ();
-                               return this[column, version];
+                               return this[columnIndex, version];
                        }
                }
 
@@ -175,31 +182,10 @@ namespace System.Data {
                /// </summary>
                public object this[DataColumn column, DataRowVersion version] {
                        get {
-                               if (column == null)
-                                       throw new ArgumentNullException ();     
-
-                               int columnIndex = _table.Columns.IndexOf (column);
-
-                               if (columnIndex == -1)
-                                       throw new ArgumentException ();
-
-                               if (version == DataRowVersion.Default)
-                                       return column.DefaultValue;
-
-                               if (!HasVersion (version))
-                                       throw new VersionNotFoundException ();
-
-                               switch (version)
-                               {
-                                       case DataRowVersion.Proposed:
-                                               return proposed[columnIndex];
-                                       case DataRowVersion.Current:
-                                               return current[columnIndex];
-                                       case DataRowVersion.Original:
-                                               return original[columnIndex];
-                                       default:
-                                               throw new ArgumentException ();
-                               }
+                               if (column.Table != Table)
+                                       throw new ArgumentException ("The column does not belong to this table.");
+                               int columnIndex = column.Ordinal;
+                               return this[columnIndex, version];
                        }
                }
 
@@ -208,53 +194,236 @@ namespace System.Data {
                /// retrieve.
                /// </summary>
                public object this[int columnIndex, DataRowVersion version] {
-                       [MonoTODO]
                        get {
-                               DataColumn column = _table.Columns[columnIndex]; //FIXME: throws
-                               if (column == null) 
+                               if (columnIndex < 0 || columnIndex > _table.Columns.Count)
                                        throw new IndexOutOfRangeException ();
-                               return this[column, version];
+                               // Accessing deleted rows
+                               if (!_inExpressionEvaluation && rowState == DataRowState.Deleted && version != DataRowVersion.Original)
+                                       throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
+                               
+                               DataColumn col = _table.Columns[columnIndex];
+                               if (col.Expression != String.Empty) {
+                                       object o = col.CompiledExpression.Eval (this);
+                                       return Convert.ChangeType (o, col.DataType);
+                               }
+                               
+                               if (HasVersion(version))
+                               {
+                                       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 ();
+                                       }
+                               }
+                               if (rowState == DataRowState.Detached && version == DataRowVersion.Default && proposed == null)
+                                       throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
+                               
+                               throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
                        }
                }
+               
+               internal void SetOriginalValue (string columnName, object val)
+               {
+                       int columnIndex = _table.Columns.IndexOf (columnName);
+                       DataColumn column = _table.Columns[columnIndex];
+                       _table.ChangingDataColumn (this, column, val);
+                               
+                       if (original == null) original = new object [_table.Columns.Count];
+                       val = SetColumnValue (val, column, columnIndex);
+                       original[columnIndex] = val;
+                       rowState = DataRowState.Modified;
+               }
 
                /// <summary>
                /// Gets or sets all of the values for this row through an array.
                /// </summary>
-               [MonoTODO]
                public object[] ItemArray {
-                       get { return current; }
+                       get { 
+                               // row not in table
+                               if (rowState == DataRowState.Detached)
+                                       throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
+                               // Accessing deleted rows
+                               if (rowState == DataRowState.Deleted)
+                                       throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
+                               
+                               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++) {
 
-                               for (int i = 0; i < value.Length; i += 1)
-                               {
-                                       if (_table.Columns[i].ReadOnly && value[i] != this[i])
-                                               throw new ReadOnlyException ();
+                                       if (i < value.Length)
+                                               v = value[i];
+                                       else
+                                               v = null;
+
+                                       newItems[i] = SetColumnValue (v, _table.Columns[i], i);
+                               }
+
+                               bool orginalEditing = editing;
+                               if (!orginalEditing) BeginEdit ();
+                               proposed = newItems;
+                               if (!orginalEditing) EndEdit ();
+                       }
+               }
+
+               private object SetColumnValue (object v, DataColumn col, int index) 
+               {               
+                       object newval = null;
+                       
+                       if (_hasParentCollection && col.ReadOnly && v != this[index])
+                               throw new ReadOnlyException ();
+
+                       if (v == null)
+                       {
+                               if (col.DataType.ToString().Equals("System.Guid"))
+                                       throw new ArgumentException("Cannot set column to be null, Please use DBNull instead");
 
-                                       if (value[i] == null)
+                               if(col.DefaultValue != DBNull.Value) 
+                               {
+                                       newval = col.DefaultValue;
+                               }
+                               else if(col.AutoIncrement == true && CanAccess(index,DataRowVersion.Default)) 
+                               {
+                                       // AutoIncrement column is already filled,
+                                       // so it just need to return existing value.
+                                       newval = this [index];
+                               }
+                               else 
+                               {
+                                       if (!col.AllowDBNull)
                                        {
-                                               if (!_table.Columns[i].AllowDBNull)
-                                                       throw new NoNullAllowedException ();
-                                               continue;
+                                               //Constraint violations during data load is raise in DataTable EndLoad
+                                               this._nullConstraintViolation = true;
+                                               if (this.Table._duringDataLoad) {
+                                                       this.Table._nullConstraintViolationDuringDataLoad = true;
+                                               }
+                                               _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
                                        }
-                                               
-                                       //FIXME: int strings can be converted to ints
-                                       if (_table.Columns[i].DataType != value[i].GetType())
-                                               throw new InvalidCastException ();
-                               }
 
-                               //FIXME: BeginEdit() not correct 
-                               BeginEdit ();  // implicitly called
-                               rowState = DataRowState.Modified;
+                                       newval = DBNull.Value;
+                               }
+                       }               
+                       else if (v == DBNull.Value) 
+                       {
+                               
+                               if (!col.AllowDBNull)
+                               {
+                                       //Constraint violations during data load is raise in DataTable EndLoad
+                                       this._nullConstraintViolation = true;
+                                       if (this.Table._duringDataLoad) {
+                                                       this.Table._nullConstraintViolationDuringDataLoad = true;
+                                       }
+                                       _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
+                               }
+                               
+                               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:
+                                                               if (!cType.IsArray)
+                                                                       throw new InvalidCastException("Type not supported.");
+                                                               break;
+                                               }
+                                                       break;
+                                       }
+                                       vType = v.GetType();
+                               }
 
-                               //FIXME: this isn't correct.  a shorter array can set the first few values
-                               //and not touch the rest.  So not all the values will get replaced
-                               proposed = value;
+                               // The MaxLength property is ignored for non-text columns
+                               if ((Type.GetTypeCode(vType) == TypeCode.String) && (col.MaxLength != -1) && 
+                                       (this.Table.Columns[index].MaxLength < ((string)v).Length)) {
+                                       throw new ArgumentException("Cannot set column '" + col.ColumnName + "' to '" + v + "'. The value violates the MaxLength limit of this column.");
+                               }
+                               newval = v;
+                               if(col.AutoIncrement == true) {
+                                       long inc = Convert.ToInt64(v);
+                                       col.UpdateAutoIncrementValue (inc);
+                               }
                        }
+                       col.DataHasBeenSet = true;
+                       return newval;
                }
 
                /// <summary>
@@ -275,8 +444,19 @@ namespace System.Data {
 
                //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 DataRowState RowStateInternal {
-                       set { rowState = value;}
+               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;
+                       _rowId = -1;
+                       _hasParentCollection = false;
+                       rowState = DataRowState.Detached;
                }
 
                /// <summary>
@@ -286,6 +466,28 @@ namespace System.Data {
                        get { return _table; }
                }
 
+               internal XmlDataDocument.XmlDataElement DataElement {
+                       get { return mappedElement; }
+                       set { mappedElement = value; }
+               }
+
+               /// <summary>
+               /// Gets and sets index of row. This is used from 
+               /// XmlDataDocument.
+               // </summary>
+               internal int XmlRowID {
+                       get { return xmlRowID; }
+                       set { xmlRowID = value; }
+               }
+               
+               /// <summary>
+               /// Gets and sets index of row.
+               // </summary>
+               internal int RowID {
+                       get { return _rowId; }
+                       set { _rowId = value; }
+               }
+
                #endregion
 
                #region Methods
@@ -294,74 +496,57 @@ namespace System.Data {
                /// Commits all the changes made to this row since the last time AcceptChanges was
                /// called.
                /// </summary>
-               [MonoTODO]
                public void AcceptChanges () 
                {
-                       
-                       if(rowState == DataRowState.Added)
-                       {
-                               //Instantiate original and proposed values so that we can call
-                               //EndEdit()
-                               this.BeginEdit();
-                       }
-
-                       this.EndEdit ();
-
-                       switch (rowState)
-                       {
-                               case DataRowState.Added:
-                               case DataRowState.Detached:
-                               case DataRowState.Modified:
-                                       rowState = DataRowState.Unchanged;
-                                       break;
-                               case DataRowState.Deleted:
-                                       _table.Rows.Remove (this); //FIXME: this should occur in end edit
-                                       break;
+                       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.RemoveInternal (this);
+                               DetachRow();
+                               break;
+                       case DataRowState.Detached:
+                               throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
                        }
-
-                       //MS implementation assigns the Proposed values
-                       //to both current and original and keeps original after calling AcceptChanges
-                       //Copy proposed to original in this.EndEdit()
-                       //original = null;
+                       // 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() 
+               public void BeginEdit () 
                {
+                       
+                       if (_inChangingEvent)
+                                throw new InRowChangingEventException("Cannot call BeginEdit inside an OnRowChanging event.");
                        if (rowState == DataRowState.Deleted)
                                throw new DeletedRowInaccessibleException ();
-
-                       if (!HasVersion (DataRowVersion.Proposed))
-                       {
+                       if (!HasVersion (DataRowVersion.Proposed)) {
                                proposed = new object[_table.Columns.Count];
-                               Array.Copy (current, proposed, _table.Columns.Count);
-                       }
-                       //TODO: Suspend validation
-
-                       //FIXME: this doesn't happen on begin edit
-                       if (!HasVersion (DataRowVersion.Original))
-                       {
-                               original = new object[_table.Columns.Count];
-                               Array.Copy (current, original, _table.Columns.Count);
+                               Array.Copy (current, proposed, current.Length);
                        }
+                       // setting editing to true stops validations on the row
+                       editing = true;
                }
 
                /// <summary>
                /// Cancels the current edit on the row.
                /// </summary>
-               [MonoTODO]
                public void CancelEdit () 
                {
-                       //FIXME: original doesn't get erased on CancelEdit
-                       //TODO: Events
-                       if (HasVersion (DataRowVersion.Proposed))
-                       {
-                               original = null;
+                        if (_inChangingEvent)
+                                throw new InRowChangingEventException("Cannot call CancelEdit inside an OnRowChanging event.");
+                       editing = false;
+                       if (HasVersion (DataRowVersion.Proposed)) {
                                proposed = null;
-                               rowState = DataRowState.Unchanged;
+                               if (rowState == DataRowState.Modified)
+                                   rowState = DataRowState.Unchanged;
                        }
                }
 
@@ -378,84 +563,333 @@ namespace System.Data {
                /// <summary>
                /// Deletes the DataRow.
                /// </summary>
-               [MonoTODO]
                public void Delete () 
                {
+                       _table.DeletingDataRow(this, DataRowAction.Delete);
                        switch (rowState) {
                        case DataRowState.Added:
-                               Table.Rows.Remove (this);
+                               // check what to do with child rows
+                               CheckChildRows(DataRowAction.Delete);
+                               _table.DeleteRowFromIndexes (this);
+                               Table.Rows.RemoveInternal (this);
+
+                               // if row was in Added state we move it to Detached.
+                               DetachRow();
                                break;
                        case DataRowState.Deleted:
-                               throw new DeletedRowInaccessibleException ();
+                               break;          
                        default:
-                               //TODO: Events, Constraints
+                               // check what to do with child rows
+                               CheckChildRows(DataRowAction.Delete);
+                               _table.DeleteRowFromIndexes (this);
                                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 relation 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 ForeignKeyConstrain of the table.
+                                       foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
+                                       {
+                                               if (fk.RelatedTable == _table)
+                                               {
+                                                       Rule rule;
+                                                       if (action == DataRowAction.Delete)
+                                                               rule = fk.DeleteRule;
+                                                       else
+                                                               rule = fk.UpdateRule;
+                                                       CheckChildRows(fk, action, rule);
+                                               }                       
+                                       }
+                               }
+                       }
+               }
+
+               private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
+               {                               
+                       DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
+                       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 < fkc.Columns.Length; k++)
+                                                                       childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[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 " + fkc.ConstraintName +", and changing this row will strand child rows.";
+                                                               string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", 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 < fkc.Columns.Length; k++)
+                                                                       child[fkc.Columns[k]] = fkc.Columns[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 < fkc.Columns.Length; k++)
+                                                                       child.SetNull(fkc.Columns[k]);
+                                                       }
+                                               }
+                                       }
+                                       break;
+                       }
+
                }
 
                /// <summary>
                /// Ends the edit occurring on the row.
                /// </summary>
-               [MonoTODO]
                public void EndEdit () 
                {
+                       if (_inChangingEvent)
+                               throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
+                       if (rowState == DataRowState.Detached)
+                       {
+                               editing = false;
+                               return;
+                       }
+                       
+                       CheckReadOnlyStatus();
                        if (HasVersion (DataRowVersion.Proposed))
                        {
-                               rowState = DataRowState.Modified;
+                               _inChangingEvent = true;
+                               try
+                               {
+                                       _table.ChangingDataRow(this, DataRowAction.Change);
+                               }
+                               finally
+                               {
+                                       _inChangingEvent = false;
+                               }
+                               if (rowState == DataRowState.Unchanged)
+                                       rowState = DataRowState.Modified;
                                
                                //Calling next method validates UniqueConstraints
                                //and ForeignKeys.
-                               _table.Rows.ValidateDataRowInternal(this);
-                               
-                               Array.Copy (proposed, current, _table.Columns.Count);
-                               
-                               //FIXME: MS implementation assigns the proposed values to
-                               //the original values. Should this be done here or on the
-                               //AcceptChanges() method?
-                               Array.Copy (proposed, original, _table.Columns.Count);
+                               try
+                               {
+                                       if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
+                                               _table.Rows.ValidateDataRowInternal(this);
+                               }
+                               catch (Exception e)
+                               {
+                                       editing = false;
+                                       proposed = null;
+                                       throw e;
+                               }
 
-                               proposed = null;
+                               // Now we are going to check all child rows of current row.
+                               // In the case the cascade is true the child rows will look up for
+                               // parent row. since lookup in index is always on current,
+                               // we have to move proposed version of current row to current
+                               // in the case of check child row failure we are rolling 
+                               // current row state back.
+                               object[] backup = current;
+                               current = proposed;
+                               bool editing_backup = editing;
+                               editing = false;
+                               try {
+                                       // check all child rows.
+                                       CheckChildRows(DataRowAction.Change);
+                                       proposed = null;
+                               }
+                               catch (Exception ex) {
+                                       // if check child rows failed - rollback to previous state
+                                       // i.e. restore proposed and current versions
+                                       proposed = current;
+                                       current = backup;
+                                       editing = editing_backup;
+                                       // since we failed - propagate an exception
+                                       throw ex;
+                               }
+                               _table.ChangedDataRow(this, DataRowAction.Change);
                        }
                }
 
                /// <summary>
                /// Gets the child rows of this DataRow using the specified DataRelation.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetChildRows (DataRelation relation) 
                {
-                       throw new NotImplementedException ();
+                       return GetChildRows (relation, DataRowVersion.Current);
                }
 
                /// <summary>
                /// Gets the child rows of a DataRow using the specified RelationName of a
                /// DataRelation.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetChildRows (string relationName) 
                {
-                       throw new NotImplementedException ();
+                       return GetChildRows (Table.DataSet.Relations[relationName]);
                }
 
                /// <summary>
                /// Gets the child rows of a DataRow using the specified DataRelation, and
                /// DataRowVersion.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
                {
-                       throw new NotImplementedException ();
+                       if (relation == null)
+                               return new DataRow[0];
+
+                       if (this.Table == null || RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
+
+                       if (relation.DataSet != this.Table.DataSet)
+                               throw new ArgumentException();
+
+                       if (relation.ChildKeyConstraint != null)
+                               return GetChildRows (relation.ChildKeyConstraint, version);
+
+                       ArrayList rows = new ArrayList();
+                       DataColumn[] parentColumns = relation.ParentColumns;
+                       DataColumn[] childColumns = relation.ChildColumns;
+                       int numColumn = parentColumns.Length;
+                       if (HasVersion(version))
+                       {
+                               object[] vals = new object[parentColumns.Length];
+                               for (int i = 0; i < vals.Length; i++)
+                                       vals[i] = this[parentColumns[i], 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 (!vals[columnCnt].Equals(
+                                                               row[childColumns[columnCnt], DataRowVersion.Default])) 
+                                                       {
+                                                               allColumnsMatch = false;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       if (allColumnsMatch) rows.Add(row);
+                               }
+                       }
+                       DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
+                       rows.CopyTo(result, 0);
+                       return result;
                }
 
                /// <summary>
                /// Gets the child rows of a DataRow using the specified RelationName of a
                /// DataRelation, and DataRowVersion.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
                {
-                       throw new NotImplementedException ();
+                       return GetChildRows (Table.DataSet.Relations[relationName], version);
+               }
+
+               private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version) 
+               {
+                       ArrayList rows = new ArrayList();
+                       DataColumn[] parentColumns = fkc.RelatedColumns;
+                       DataColumn[] childColumns = fkc.Columns;
+                       int numColumn = parentColumns.Length;
+                       if (HasVersion(version))
+                       {
+                               object[] vals = new object[parentColumns.Length];
+                               for (int i = 0; i < vals.Length; i++)
+                                       vals[i] = this[parentColumns[i], version];
+
+                               Index index = fkc.Index;
+                               if (index != null) {
+                                       // get the child rows from the index
+                                       Node[] childNodes = index.FindAllSimple (vals);
+                                       for (int i = 0; i < childNodes.Length; i++) {
+                                               rows.Add (childNodes[i].Row);
+                                       }
+                               }
+                               else { // if there is no index we search manualy.
+                                       foreach (DataRow row in fkc.Table.Rows) 
+                                       {
+                                               bool allColumnsMatch = false;
+                                               if (row.HasVersion(DataRowVersion.Default))
+                                               {
+                                                       allColumnsMatch = true;
+                                                       for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
+                                                       {
+                                                               if (!vals[columnCnt].Equals(
+                                                                       row[childColumns[columnCnt], DataRowVersion.Default])) 
+                                                               {
+                                                                       allColumnsMatch = false;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               if (allColumnsMatch) rows.Add(row);
+                                       }
+                               }
+                       }
+
+                       DataRow[] result = fkc.Table.NewRowArray(rows.Count);
+                       rows.CopyTo(result, 0);
+                       return result;
                }
 
                /// <summary>
@@ -474,7 +908,10 @@ namespace System.Data {
                        if (columnIndex < 0 || columnIndex >= columnErrors.Length)
                                throw new IndexOutOfRangeException ();
 
-                       return columnErrors[columnIndex];
+                       string retVal = columnErrors[columnIndex];
+                       if (retVal == null)
+                               retVal = string.Empty;
+                       return retVal;
                }
 
                /// <summary>
@@ -494,89 +931,134 @@ namespace System.Data {
 
                        for (int i = 0; i < columnErrors.Length; i += 1)
                        {
-                               if (columnErrors[i] != String.Empty)
+                               if (columnErrors[i] != null && columnErrors[i] != String.Empty)
                                        dataColumns.Add (_table.Columns[i]);
                        }
 
-                       return (DataColumn[])(dataColumns.ToArray ());
+                       return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
                }
 
                /// <summary>
                /// Gets the parent row of a DataRow using the specified DataRelation.
                /// </summary>
-               [MonoTODO]
                public DataRow GetParentRow (DataRelation relation) 
                {
-                       throw new NotImplementedException ();
+                       return GetParentRow (relation, DataRowVersion.Current);
                }
 
                /// <summary>
                /// Gets the parent row of a DataRow using the specified RelationName of a
                /// DataRelation.
                /// </summary>
-               [MonoTODO]
                public DataRow GetParentRow (string relationName) 
                {
-                       throw new NotImplementedException ();
+                       return GetParentRow (relationName, DataRowVersion.Current);
                }
 
                /// <summary>
                /// Gets the parent row of a DataRow using the specified DataRelation, and
                /// DataRowVersion.
                /// </summary>
-               [MonoTODO]
                public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
                {
-                       throw new NotImplementedException ();
+                       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>
-               [MonoTODO]
                public DataRow GetParentRow (string relationName, DataRowVersion version) 
                {
-                       throw new NotImplementedException ();
+                       return GetParentRow (Table.DataSet.Relations[relationName], version);
                }
 
                /// <summary>
                /// Gets the parent rows of a DataRow using the specified DataRelation.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetParentRows (DataRelation relation) 
                {
-                       throw new NotImplementedException ();
+                       return GetParentRows (relation, DataRowVersion.Current);
                }
 
                /// <summary>
                /// Gets the parent rows of a DataRow using the specified RelationName of a 
                /// DataRelation.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetParentRows (string relationName) 
                {
-                       throw new NotImplementedException ();
+                       return GetParentRows (relationName, DataRowVersion.Current);
                }
 
                /// <summary>
                /// Gets the parent rows of a DataRow using the specified DataRelation, and
                /// DataRowVersion.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
                {
-                       throw new NotImplementedException ();
+                       // TODO: Caching for better preformance
+                       if (relation == null)
+                               return new DataRow[0];
+
+                       if (this.Table == null || RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
+
+                       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))
+                       {
+                               object[] vals = new object[childColumns.Length];
+                               for (int i = 0; i < vals.Length; i++)
+                                       vals[i] = this[childColumns[i], version];
+                               
+                               Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
+                               if (indx != null) { // get the child rows from the index
+                                       Node[] childNodes = indx.FindAllSimple (vals);
+                                       for (int i = 0; i < childNodes.Length; i++) {
+                                               rows.Add (childNodes[i].Row);
+                                       }
+                               }
+                               else { // no index so we have to search manualy.
+                                       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);
+                                       }
+                               }
+                       }
+
+                       DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
+                       rows.CopyTo(result, 0);
+                       return result;
                }
 
                /// <summary>
                /// Gets the parent rows of a DataRow using the specified RelationName of a 
                /// DataRelation, and DataRowVersion.
                /// </summary>
-               [MonoTODO]
                public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
                {
-                       throw new NotImplementedException ();
+                       return GetParentRows (Table.DataSet.Relations[relationName], version);
                }
 
                /// <summary>
@@ -587,12 +1069,22 @@ namespace System.Data {
                        switch (version)
                        {
                                case DataRowVersion.Default:
+                                       if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
+                                               return false;
+                                       if (rowState == DataRowState.Detached)
+                                               return proposed != null;
                                        return true;
                                case DataRowVersion.Proposed:
+                                       if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
+                                               return false;
                                        return (proposed != null);
                                case DataRowVersion.Current:
+                                       if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
+                                               return false;
                                        return (current != null);
                                case DataRowVersion.Original:
+                                       if (rowState == DataRowState.Detached)
+                                               return false;
                                        return (original != null);
                        }
                        return false;
@@ -603,7 +1095,8 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (DataColumn column) 
                {
-                       return (this[column] == null);
+                       object o = this[column];
+                       return (o == DBNull.Value);
                }
 
                /// <summary>
@@ -612,7 +1105,8 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (int columnIndex) 
                {
-                       return (this[columnIndex] == null);
+                       object o = this[columnIndex];
+                       return (o == DBNull.Value);
                }
 
                /// <summary>
@@ -620,7 +1114,8 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (string columnName) 
                {
-                       return (this[columnName] == null);
+                       object o = this[columnName];
+                       return (o == DBNull.Value);
                }
 
                /// <summary>
@@ -629,7 +1124,25 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (DataColumn column, DataRowVersion version) 
                {
-                       return (this[column, version] == null);
+                       object o = this[column, version];
+                       return (o == DBNull.Value);
+               }
+
+               /// <summary>
+               /// Returns a value indicating whether all of the row columns specified contain a null value.
+               /// </summary>
+               internal bool IsNullColumns(DataColumn[] columns)
+               {
+                       bool allNull = true;
+                       for (int i = 0; i < columns.Length; i++) 
+                       {
+                               if (!IsNull(columns[i])) 
+                               {
+                                       allNull = false;
+                                       break;
+                               }
+                       }
+                       return allNull;
                }
 
                /// <summary>
@@ -637,23 +1150,47 @@ namespace System.Data {
                /// </summary>
                public void RejectChanges () 
                {
+                       if (RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
                        // 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);
+                                               _table.DeleteRowFromIndexes (this);
+                                               _table.Rows.RemoveInternal (this);
                                                break;
                                        case DataRowState.Modified:
+                                               if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
+                                                       _table.Rows.ValidateDataRowInternal(this);
                                                rowState = DataRowState.Unchanged;
                                                break;
                                        case DataRowState.Deleted:
                                                rowState = DataRowState.Unchanged;
+                                               if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
+                                                       _table.Rows.ValidateDataRowInternal(this);
                                                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.DeleteRowFromIndexes (this);
+                                       _table.Rows.RemoveInternal (this);
+                                       // if row was in Added state we move it to Detached.
+                                       DetachRow();
                                }
                        }
                }
@@ -687,32 +1224,298 @@ namespace System.Data {
                /// <summary>
                /// Sets the value of the specified DataColumn to a null value.
                /// </summary>
-               [MonoTODO]
                protected void SetNull (DataColumn column) 
                {
-                       throw new NotImplementedException ();
+                       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 ();
+                       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) 
                {
-                       throw new NotImplementedException ();
+                       if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
+
+                       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;
+                               DataColumn column = row.Table.Columns[columnName];
+                               //if a column with the same name exists in both rows copy the values
+                               if(column != null) {
+                                       int index = column.Ordinal;
+                                       if (HasVersion(DataRowVersion.Original))
+                                       {
+                                               if (row.original == null)
+                                                       row.original = new object[row.Table.Columns.Count];
+                                               row.original[index] = row.SetColumnValue(original[i], column, index);
+                                       }
+                                       if (HasVersion(DataRowVersion.Current))
+                                       {
+                                               if (row.current == null)
+                                                       row.current = new object[row.Table.Columns.Count];
+                                               row.current[index] = row.SetColumnValue(current[i], column, index);
+                                       }
+                                       if (HasVersion(DataRowVersion.Proposed))
+                                       {
+                                               if (row.proposed == null)
+                                                       row.proposed = new object[row.Table.Columns.Count];
+                                               row.proposed[index] = row.SetColumnValue(proposed[i], column, index);
+                                       }
+                                       
+                                       //Saving the current value as the column value
+                                       row[index] = row.current[index];
+                                       
+                               }
+                       }
+
+                       row.rowState = RowState;
+                       row.RowError = RowError;
+                       row.columnErrors = columnErrors;
+               }
+
+               
+               private 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;
+                               int index = this.Table.Columns.Count - 1;
+                               if (current != null)
+                               {
+                                       tmp = new object [index + 1];
+                                       Array.Copy (current, tmp, current.Length);
+                                       tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
+                                       current = tmp;
+                               }
+                               if (proposed != null)
+                               {
+                                       tmp = new object [index + 1];
+                                       Array.Copy (proposed, tmp, proposed.Length);
+                                       tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
+                                       proposed = tmp;
+                               }
+                               if(original != null)
+                               {
+                                       tmp = new object [index + 1];
+                                       Array.Copy (original, tmp, original.Length);
+                                       tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
+                                       original = tmp;
+                               }
+
+                       }
+               }
+
+               internal void onColumnRemoved(int columnIndex)
+               {
+                       // when column removed we have to compress row values in the way 
+                       // they will correspond to new column ordinals
+
+                       object[] tmp;
+                       if (current != null)
+                       {
+                               tmp = new object[current.Length - 1];
+                               // copy values before removed column
+                               if (columnIndex > 0)
+                                       Array.Copy (current, 0, tmp, 0, columnIndex);
+                               // copy values after removed column
+                               if(columnIndex < current.Length - 1)
+                                       Array.Copy(current, columnIndex + 1, tmp, columnIndex, current.Length - 1 - columnIndex);
+
+                               current = tmp;
+                       }
+                       if (proposed != null)
+                       {
+                               tmp = new object[proposed.Length - 1];
+                               // copy values before removed column
+                               if (columnIndex > 0)
+                                       Array.Copy (proposed, 0, tmp, 0, columnIndex);
+                               // copy values after removed column
+                               if(columnIndex < proposed.Length - 1)
+                                       Array.Copy(proposed, columnIndex + 1, tmp, columnIndex, proposed.Length - 1 - columnIndex);
+
+                               proposed = tmp;
+                       }
+                       if (original != null)
+                       {
+                               tmp = new object[original.Length - 1];
+                               // copy values before removed column
+                               if (columnIndex > 0)
+                                       Array.Copy (original, 0, tmp, 0, columnIndex);
+                               // copy values after removed column
+                               if(columnIndex < original.Length - 1)
+                                       Array.Copy(original, columnIndex + 1, tmp, columnIndex, original.Length - 1 - columnIndex);
+
+                               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;
+                       }
+               }
+
+               // checks existance of value in version pecified
+               // note : this one relies on the same algorithm as this[int,DataRowVersion]
+               private bool CanAccess(int columnIndex, DataRowVersion version) 
+               {
+                       if (columnIndex < 0 || columnIndex > _table.Columns.Count)
+                               return false;
+                       // Accessing deleted rows
+                       if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
+                               return false;
+
+                       if (HasVersion(version))
+                       {
+                               object[] versionArr;
+                               switch (version) 
+                               {
+                                       case DataRowVersion.Default:
+                                               if (editing || rowState == DataRowState.Detached) {
+                                                       versionArr = proposed;
+                                               }
+                                               else {
+                                                       versionArr = current;
+                                               }
+                                               break;
+                                       case DataRowVersion.Proposed:
+                                               versionArr = proposed;
+                                               break;
+                                       case DataRowVersion.Current:
+                                               versionArr = current;
+                                               break;
+                                       case DataRowVersion.Original:
+                                               versionArr = original;
+                                               break;
+                                       default:
+                                               return false;
+                               }
+
+                               return (versionArr != null && columnIndex < versionArr.Length);
+                       }
+                       
+                       return false;
+               }
+
+               internal void CheckNullConstraints()
+               {
+                       if (_nullConstraintViolation) {
+                               if (proposed != null) {
+                                       for (int i = 0; i < proposed.Length; i++) {
+                                               if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
+                                                       throw new NoNullAllowedException(_nullConstraintMessage);
+                                       }
+                               }
+                               _nullConstraintViolation = false;
+                       }
+               }
                
+               internal void CheckReadOnlyStatus()
+                {
+                       if (proposed == null)
+                               return;
+
+                       for (int i = 0; i < proposed.Length; i++) {
+                               if (this[i] != proposed[i] && _table.Columns[i].ReadOnly)
+                                       throw new ReadOnlyException();
+                        }
+                           
+                }
+       
                #endregion // Methods
        }
+
+       
+
 }