2004-03-10 Umadevi S (sumadevi@novell.com)
[mono.git] / mcs / class / System.Data / System.Data / DataRowCollection.cs
index ca98c053d44e89df57eb237006cd92dba719995e..1debc20ca4260867b88ff1a61fa4c01d64b1c999 100644 (file)
@@ -7,6 +7,7 @@
 //
 // (C) Ximian, Inc 2002
 // (C) Copyright 2002 Tim Coleman
+// (C) Copyright 2002 Daniel Morgan
 //
 
 using System;
@@ -26,7 +27,7 @@ namespace System.Data
                /// <summary>
                /// Internal constructor used to build a DataRowCollection.
                /// </summary>
-               protected internal DataRowCollection (DataTable table) : base ()
+               internal DataRowCollection (DataTable table) : base ()
                {
                        this.table = table;
                }
@@ -36,7 +37,12 @@ namespace System.Data
                /// </summary>
                public DataRow this[int index] 
                {
-                       get { return (DataRow) list[index]; }
+                       get { 
+                               if (index >= Count)
+                                       throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
+
+                               return (DataRow) list[index]; 
+                       }
                }
 
                /// <summary>
@@ -52,10 +58,28 @@ namespace System.Data
                /// </summary>
                public void Add (DataRow row) 
                {
-                       //TODO: AutoIncrement
                        //TODO: validation
+                       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 row id is not -1, we know that it is in the collection.
+                       if (row.RowID != -1)
+                               throw new ArgumentException ("This row already belongs to this table.");
+                       
+
+                       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.RowStateInternal = DataRowState.Added;
+                       // Set the row id.
+                       row.RowID = list.Count - 1;
+                       row.AttachRow ();
+                       row.Table.ChangedDataRow (row, DataRowAction.Add);
                }
 
                /// <summary>
@@ -72,9 +96,22 @@ namespace System.Data
                /// <summary>
                /// Clears the collection of all rows.
                /// </summary>
-               [MonoTODO]
                public void Clear () 
                {
+                       if (this.table.DataSet != null)
+                       {
+                               foreach (DataTable table in this.table.DataSet.Tables)
+                               {
+                                       foreach (Constraint c in table.Constraints)
+                                       {
+                                               if (c is ForeignKeyConstraint)
+                                               {
+                                                       if (((ForeignKeyConstraint) c).RelatedTable.Equals(this.table))
+                                                               throw new InvalidConstraintException("Cannot clear table Parent because ForeignKeyConstraint " + c.ConstraintName + " enforces Child.");
+                                               }
+                                       }
+                               }
+                       }
                        list.Clear ();
                }
 
@@ -82,20 +119,22 @@ namespace System.Data
                /// Gets a value indicating whether the primary key of any row in the collection contains
                /// the specified value.
                /// </summary>
-               [MonoTODO]
                public bool Contains (object key) 
                {
-                       throw new NotImplementedException ();
+                       return Find (key) != null;
                }
 
                /// <summary>
                /// Gets a value indicating whether the primary key column(s) of any row in the 
                /// collection contains the values specified in the object array.
                /// </summary>
-               [MonoTODO]
                public bool Contains (object[] keys) 
                {
-                       throw new NotImplementedException ();
+                       if (table.PrimaryKey.Length != keys.Length)
+                               throw new ArgumentException ("Expecting " + table.PrimaryKey.Length + " value(s) for the key " + 
+                                                            "being indexed, but received " + keys.Length + " value(s).");
+
+                       return Find (keys) != null;
                }
 
                /// <summary>
@@ -104,7 +143,38 @@ namespace System.Data
                [MonoTODO]
                public DataRow Find (object key) 
                {
-                       throw new NotImplementedException ();
+                       if (table.PrimaryKey.Length == 0)
+                               throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
+                       if (table.PrimaryKey.Length > 1)
+                               throw new ArgumentException ("Expecting " + table.PrimaryKey.Length + 
+                                                            " value(s) for the key being indexed, but received 1 value(s).");
+
+                       string primColumnName = table.PrimaryKey [0].ColumnName;
+                       Type coltype = null;
+                       object newKey = null;
+                       
+                       foreach (DataRow row in this) {
+                               
+                               if (row.RowState != DataRowState.Deleted)
+                               {
+                                       object primValue = row [primColumnName];
+                                       if (key == null) 
+                                       {
+                                               if (primValue == null)
+                                                       return row;
+                                               else 
+                                                       continue;
+                                       }
+                                      
+                                       newKey = Convert.ChangeType (key, Type.GetTypeCode(primValue.GetType ()));
+
+                                       if (primValue.Equals (newKey))
+                                               return row;
+                               }
+                       }
+                                               
+                       // FIXME: is the correct value null?
+                       return null;
                }
 
                /// <summary>
@@ -113,7 +183,51 @@ namespace System.Data
                [MonoTODO]
                public DataRow Find (object[] keys) 
                {
-                       throw new NotImplementedException ();
+                       if (table.PrimaryKey.Length == 0)
+                               throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
+
+                       string  [] primColumnNames = new string [table.PrimaryKey.Length];
+                       
+                       for (int i = 0; i < primColumnNames.Length; i++)
+                               primColumnNames [i] = table.PrimaryKey [i].ColumnName;
+
+                       Type coltype = null;
+                       object newKey = null;
+                       
+                       foreach (DataRow row in this) {
+                               
+                               if (row.RowState != DataRowState.Deleted)
+                               {
+                                       bool eq = true;
+                                       for (int i = 0; i < keys.Length; i++) 
+                                       {
+                                       
+                                               object primValue = row [primColumnNames [i]];
+                                               object keyValue = keys [i];
+                                               if (keyValue == null) 
+                                               {
+                                                       if (primValue == null)
+                                                               return row;
+                                                       else 
+                                                               continue;
+                                               }
+                                                                      
+                                               newKey = Convert.ChangeType (keyValue, Type.GetTypeCode(primValue.GetType ()));
+
+                                               if (!primValue.Equals (newKey)) 
+                                               {
+                                                       eq = false;
+                                                       break;
+                                               }                                               
+                                       }
+
+                                       if (eq)
+                                               return row;
+                               }
+                       }
+                                               
+                       // FIXME: is the correct value null?
+                       return null;
                }
 
                /// <summary>
@@ -121,7 +235,45 @@ namespace System.Data
                /// </summary>
                public void InsertAt (DataRow row, int pos) 
                {
-                       list.Insert (pos, row);
+                       if (pos < 0)
+                               throw new IndexOutOfRangeException ("The row insert position " + pos + " is invalid.");
+                       
+                       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 row id is not -1, we know that it is in the collection.
+                       if (row.RowID != -1)
+                               throw new ArgumentException ("This row already belongs to this table.");
+                       
+                       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);
+                               
+                       if (pos >= list.Count)
+                               list.Add (row);
+                       else
+                               list.Insert (pos, row);
+                               
+                       row.HasParentCollection = true;
+                       row.AttachRow ();
+                       row.Table.ChangedDataRow (row, DataRowAction.Add);
+               }
+
+               /// <summary>
+               /// Removes the specified DataRow from the internal list. Used by DataRow to commit the removing.
+               /// </summary>
+               internal void RemoveInternal (DataRow row) {
+                       if (row == null) {
+                               throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
+                       }
+                       int index = list.IndexOf(row);
+                       if (index < 0) {
+                               throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
+                       }
+                       list.RemoveAt(index);
                }
 
                /// <summary>
@@ -129,16 +281,79 @@ namespace System.Data
                /// </summary>
                public void Remove (DataRow row) 
                {
-                       list.Remove (row);
+                       if (row == null)
+                               throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
+                       int index = list.IndexOf(row);
+                       if (index < 0)
+                               throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
+                       row.Delete();
+                       // if the row was in added state it will be in Detached state after the
+                       // delete operation, so we have to check it.
+                       if (row.RowState != DataRowState.Detached)
+                               row.AcceptChanges();
                }
 
                /// <summary>
                /// Removes the row at the specified index from the collection.
                /// </summary>
                public void RemoveAt (int index) 
+               {                       
+                       if (index < 0 || index >= list.Count)
+                               throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
+                       DataRow row = (DataRow)list [index];
+                       row.Delete();
+                       row.AcceptChanges();
+               }
+
+               ///<summary>
+               ///Internal method used to validate a given DataRow with respect
+               ///to the DataRowCollection
+               ///</summary>
+               [MonoTODO]
+               internal void ValidateDataRowInternal(DataRow row)
                {
-                       list.RemoveAt (index);
+                       //first check for null violations.
+                       row.CheckNullConstraints();
+                       // This validates constraints in the specific order : 
+                       // first unique/primary keys first, then Foreignkeys, etc
+                       ArrayList uniqueConstraintsDone = new ArrayList();
+                       ArrayList foreignKeyConstraintsDone = new ArrayList();
+                       try {
+                               foreach(Constraint constraint in table.Constraints.UniqueConstraints) {
+                                       constraint.AssertConstraint(row);
+                                       uniqueConstraintsDone.Add(constraint);
+                               }
+                       
+                               foreach(Constraint constraint in table.Constraints.ForeignKeyConstraints) {
+                                       constraint.AssertConstraint(row);
+                                       foreignKeyConstraintsDone.Add(constraint);
+                               }
+                       }
+                       // if one of the AssertConstraint failed - we need to "rollback" all the changes
+                       // caused by AssertCoinstraint calls already succeeded
+                       catch(ConstraintException e) {
+                               RollbackAsserts(row,foreignKeyConstraintsDone,uniqueConstraintsDone);
+                               throw e;
+                       }
+                       catch(InvalidConstraintException e) {   
+                               RollbackAsserts(row,foreignKeyConstraintsDone,uniqueConstraintsDone);
+                               throw e;
+                       }
                }
 
+               private void RollbackAsserts(DataRow row,ICollection foreignKeyConstraintsDone,
+                       ICollection uniqueConstraintsDone)
+               {
+                       // if any of constraints assert failed - 
+                       // we have to rollback all the asserts scceeded
+                       // on order reverse to thier original execution
+                       foreach(Constraint constraint in foreignKeyConstraintsDone) {
+                               constraint.RollbackAssert(row);
+                       }
+
+                       foreach(Constraint constraint in uniqueConstraintsDone) {
+                               constraint.RollbackAssert(row);
+                       }
+               }
        }
 }