2004-05-15 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.Data / System.Data / DataTable.cs
index 313756dfa7dfeef2b25075cb6816e121f4368d72..39ff652bda047835ec4e1e5b1c055c13a04d5c1d 100644 (file)
 //
 // (C) Chris Podurgiel
 // (C) Ximian, Inc 2002
-// Copyright (C) Tim Coleman, 2002
+// Copyright (C) Tim Coleman, 2002-2003
+// Copyright (C) Daniel Morgan, 2002-2003
 //
 
 using System;
 using System.Collections;
 using System.ComponentModel;
 using System.Globalization;
+using System.IO;
 using System.Runtime.Serialization;
+using System.Xml;
+using System.Xml.Schema;
+using Mono.Data.SqlExpressions;
 
 namespace System.Data {
+       //[Designer]
+       [ToolboxItem (false)]
        [DefaultEvent ("RowChanging")]
        [DefaultProperty ("TableName")]
        [DesignTimeVisible (false)]
+       [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
        [Serializable]
-       public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable        
+       public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable 
        {
                internal DataSet dataSet;   
                
@@ -40,9 +48,8 @@ namespace System.Data {
                private CultureInfo _locale;
                private int _minimumCapacity;
                private string _nameSpace;
-               // FIXME: temporarily commented
-               // private DataTableRelationCollection _childRelations; 
-               // private DataTableRelationCollection _parentRelations;
+               private DataRelationCollection _childRelations; 
+               private DataRelationCollection _parentRelations;
                private string _prefix;
                private DataColumn[] _primaryKey;
                private DataRowCollection _rows;
@@ -50,16 +57,43 @@ namespace System.Data {
                private string _tableName;
                private bool _containsListCollection;
                private string _encodedTableName;
+               internal bool _duringDataLoad;
+               internal bool _nullConstraintViolationDuringDataLoad;
+               private bool dataSetPrevEnforceConstraints;
+               private bool dataTablePrevEnforceConstraints;
+               private bool enforceConstraints = true;
+               private DataRowBuilder _rowBuilder;
+               private ArrayList _indexes;
+
+               
+               // If CaseSensitive property is changed once it does not anymore follow owner DataSet's 
+               // CaseSensitive property. So when you lost you virginity it's gone for ever
+               private bool _virginCaseSensitive = true;
                
+                /// <summary>
+                 /// An enum variable indicating whether BeginInit() or EndInit() is called.
+                /// Delegate to call a function which performs cleanup after EndInit() is called
+                /// </summary>
+                internal enum initStatus { NotInitialized, BeginInit, EndInit };
+        
+               private initStatus _initStatus;
+        
+               private delegate void PostEndInit();
+                
+               internal initStatus InitStatus{
+                        get{ return( _initStatus ); }
+                }
+       
+
                /// <summary>
                /// Initializes a new instance of the DataTable class with no arguments.
                /// </summary>
                
-               public DataTable()
+               public DataTable () 
                {
                        dataSet = null;
                        _columnCollection = new DataColumnCollection(this);
-                       _constraintCollection = new ConstraintCollection(); 
+                       _constraintCollection = new ConstraintCollection(this); 
                        _extendedProperties = new PropertyCollection();
                        _tableName = "";
                        _nameSpace = null;
@@ -68,14 +102,13 @@ namespace System.Data {
                        _primaryKey = null;
                        _site = null;
                        _rows = new DataRowCollection (this);
-                       _locale = CultureInfo.CurrentCulture;
-
+                       _indexes = new ArrayList();
+                       
                        //LAMESPEC: spec says 25 impl does 50
                        _minimumCapacity = 50;
                        
-                       // FIXME: temporaily commented DataTableRelationCollection
-                       // _childRelations = new DataTableRelationCollection();
-                       // _parentRelations = new DataTableRelationCollection();
+                       _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
+                       _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
 
                
                        _defaultView = new DataView(this);
@@ -84,8 +117,7 @@ namespace System.Data {
                /// <summary>
                /// Intitalizes a new instance of the DataTable class with the specified table name.
                /// </summary>
-               
-               public DataTable(string tableName) : this ()
+               public DataTable (string tableName) : this () 
                {
                        _tableName = tableName;
                }
@@ -93,66 +125,114 @@ namespace System.Data {
                /// <summary>
                /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
                /// </summary>
-               
                [MonoTODO]
-               protected DataTable(SerializationInfo info, StreamingContext context)
-                       : this ()
+               protected DataTable (SerializationInfo info, StreamingContext context)
+                       : this () 
                {
-                       //
-                       // TODO: Add constructor logic here
-                       //
+                       string schema = info.GetString ("XmlSchema");
+                       string data = info.GetString ("XmlDiffGram");
+
+                       DataSet ds = new DataSet ();
+                       ds.ReadXmlSchema (new StringReader (schema));
+                       ds.Tables [0].CopyProperties (this);
+                       ds = new DataSet ();
+                       ds.Tables.Add (this);
+                       ds.ReadXml (new StringReader (data), XmlReadMode.IgnoreSchema);
+                       ds.Tables.Remove (this);
+/* keeping for a while. With the change above, we shouldn't have to consider 
+ * DataTable mode in schema inference/read.
+                       XmlSchemaMapper mapper = new XmlSchemaMapper (this);
+                       XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
+                       mapper.Read (xtr);
+                       
+                       XmlDiffLoader loader = new XmlDiffLoader (this);
+                       xtr = new XmlTextReader(new StringReader (data));
+                       loader.Load (xtr);
+*/
+               }
+
+#if NET_2_0
+               public DataTable (string tableName, string tbNamespace)
+                       : this (tableName)
+               {
+                       _nameSpace = tbNamespace;
                }
+#endif
 
                /// <summary>
                /// Indicates whether string comparisons within the table are case-sensitive.
                /// </summary>
                [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]        
                public bool CaseSensitive {
-                       get { return _caseSensitive; }
-                       set { _caseSensitive = value; }
+                       get { 
+                               if (_virginCaseSensitive && dataSet != null)
+                                       return dataSet.CaseSensitive; 
+                               else
+                                       return _caseSensitive;
+                               }
+                       set {
+                               if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
+                                       throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
+                               }
+                               _virginCaseSensitive = false;
+                               _caseSensitive = value; 
+                       }
+               }
+
+               internal bool VirginCaseSensitive {
+                       get { return _virginCaseSensitive; }
+                       set { _virginCaseSensitive = value; }
                }
 
+               internal ArrayList Indexes{
+                       get { return _indexes; }
+               }
 
-               internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
+               internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv) 
                {
                        DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
                        OnColumnChanged(e);
                }
 
-               internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
+               internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv) 
                {
                        DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
                        OnColumnChanging (e);
                }
 
-               internal void DeletedDataRow (DataRow dr, DataRowAction action)
+               internal void DeletedDataRow (DataRow dr, DataRowAction action) 
                {
                        DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
                        OnRowDeleted (e);
                }
 
-               internal void ChangedDataRow (DataRow dr, DataRowAction action)
+               internal void DeletingDataRow (DataRow dr, DataRowAction action) 
+               {
+                       DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
+                       OnRowDeleting(e);
+               }
+
+               internal void ChangedDataRow (DataRow dr, DataRowAction action) 
                {
                        DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
                        OnRowChanged (e);
                }
 
+               internal void ChangingDataRow (DataRow dr, DataRowAction action) 
+               {
+                       DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
+                       OnRowChanging (e);
+               }
+
                /// <summary>
                /// Gets the collection of child relations for this DataTable.
                /// </summary>
-               [MonoTODO]      
                [Browsable (false)]
                [DataSysDescription ("Returns the child relations for this table.")]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                public DataRelationCollection ChildRelations {
                        get {
-                               // FIXME: temporarily commented to compile
-                               // return (DataRelationCollection)_childRelations;
-                               
-                               //We are going to have to Inherit a class from 
-                               //DataRelationCollection because DRC is abstract
-                               
-                               throw new NotImplementedException ();
+                               return _childRelations;
                        }
                }
 
@@ -208,7 +288,7 @@ namespace System.Data {
                [DataSysDescription ("The expression used to compute the data-bound value of this row.")]       
                [DefaultValue ("")]
                public string DisplayExpression {
-                       get { return "" + _displayExpression; }
+                       get { return _displayExpression == null ? "" : _displayExpression; }
                        set { _displayExpression = value; }
                }
 
@@ -230,7 +310,15 @@ namespace System.Data {
                [Browsable (false)]
                [DataSysDescription ("Returns whether the table has errors.")]
                public bool HasErrors {
-                       get { return _hasErrors; }
+                       get { 
+                               // we can not use the _hasError flag because we do not know when to turn it off!
+                               for (int i = 0; i < _rows.Count; i++)
+                               {
+                                       if (_rows[i].HasErrors)
+                                               return true;
+                               }
+                               return false;
+                       }
                }
 
                /// <summary>
@@ -239,8 +327,24 @@ namespace System.Data {
                /// </summary>
                [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
                public CultureInfo Locale {
-                       get { return _locale; }
-                       set { _locale = value; }
+                       get { 
+                               // if the locale is null, we check for the DataSet locale
+                               // and if the DataSet is null we return the current culture.
+                               // this way if DataSet locale is changed, only if there is no locale for 
+                               // the DataTable it influece the Locale get;
+                               if (_locale != null)
+                                       return _locale;
+                               if (DataSet != null)
+                                       return DataSet.Locale;
+                               return CultureInfo.CurrentCulture;
+                       }
+                       set { 
+                               if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
+                                       throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
+                               }
+                               if (_locale == null || !_locale.Equals(value))
+                                       _locale = value; 
+                       }
                }
 
                /// <summary>
@@ -261,7 +365,7 @@ namespace System.Data {
                [DataCategory ("Data")]
                [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
                public string Namespace {
-                       get { return "" + _nameSpace; }
+                       get { return _nameSpace == null ? "" : _nameSpace; }
                        set { _nameSpace = value; }
                }
 
@@ -269,15 +373,12 @@ namespace System.Data {
                /// Gets the collection of parent relations for 
                /// this DataTable.
                /// </summary>
-               [MonoTODO]
                [Browsable (false)]
                [DataSysDescription ("Returns the parent relations for this table.")]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                public DataRelationCollection ParentRelations {
                        get {   
-                               // FIXME: temporarily commented to compile
-                               // return _parentRelations;
-                               throw new NotImplementedException ();
+                               return _parentRelations;
                        }
                }
 
@@ -289,8 +390,15 @@ namespace System.Data {
                [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
                [DefaultValue ("")]
                public string Prefix {
-                       get { return "" + _prefix; }
-                       set { _prefix = value; }
+                       get { return _prefix == null ? "" : _prefix; }
+                       set {
+                               // Prefix cannot contain any special characters other than '_' and ':'
+                               for (int i = 0; i < value.Length; i++) {
+                                       if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
+                                               throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
+                               }
+                               _prefix = value;
+                       }
                }
 
                /// <summary>
@@ -299,39 +407,68 @@ namespace System.Data {
                /// </summary>
                [DataCategory ("Data")]
                [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
-               public DataColumn[] PrimaryKey
-               {
+               [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+               [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+               public DataColumn[] PrimaryKey {
                        get {
                                UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
                                if (null == uc) return new DataColumn[] {};
                                return uc.Columns;
                        }
                        set {
-
-                               //YUK: msft removes a previous unique constraint if it is flagged as a pk  
-                               //when a new pk is set 
-
-                               //clear Primary Key if value == null
-                               if (null == value)
-                               {
-                                       UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
+                               UniqueConstraint oldPKConstraint = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
+                               
+                               // first check if value is the same as current PK.
+                               if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
                                        return;
-                               }
-                       
 
-                               //Does constraint exist for these columns
-                               UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
+                               // remove PK Constraint
+                               if(oldPKConstraint != null) {
+                                       Constraints.Remove(oldPKConstraint);
+                               }
+                               
+                               if (value != null) {
+                                       //Does constraint exist for these columns
+                                       UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
                                                this.Constraints, (DataColumn[]) value);
-
-                               //if constraint doesn't exist for columns
-                               //create new unique primary key constraint
-                               if (null == uc)
-                               {
-                                       uc = new UniqueConstraint( (DataColumn[]) value, true);
+                               
+                                       //if constraint doesn't exist for columns
+                                       //create new unique primary key constraint
+                                       if (null == uc) {
+                                               foreach (DataColumn Col in (DataColumn[]) value) {
+
+                                                       if (Col.Table == null)
+                                                               break;
+
+                                                       if (Columns.IndexOf (Col) < 0)
+                                                               throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
+                                               }
+
+
+                                               uc = new UniqueConstraint( (DataColumn[]) value, true);
+                                       
+                                               Constraints.Add (uc);
+                                       }
+                                       else { 
+                                               //set existing constraint as the new primary key
+                                               UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
+                                       }
+
+                                       // if we really supplied some columns fo primary key - 
+                                       // rebuild indexes fo all foreign key constraints
+                                       if(value.Length > 0) {
+                                               foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
+                                                       constraint.AssertConstraint();
+                                               }
+                                       }
                                }
-                               else //set existing constraint as the new primary key
-                               {
-                                       UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
+                               else {
+                                       // if primary key is null now - drop all the indexes for foreign key constraints
+                                       foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
+                                               Index index = constraint.Index;
+                                               constraint.Index = null;
+                                               this.DropIndex(index);
+                                       }
                                }
                                
                        }
@@ -365,7 +502,7 @@ namespace System.Data {
                [DefaultValue ("")]     
                [RefreshProperties (RefreshProperties.All)]
                public string TableName {
-                       get { return "" + _tableName; }
+                       get { return _tableName == null ? "" : _tableName; }
                        set { _tableName = value; }
                }
                
@@ -375,23 +512,87 @@ namespace System.Data {
                                return false;
                        }
                }
+               
+               private bool EnforceConstraints {
+                       get { return enforceConstraints; }
+                       set {
+                               if (value != enforceConstraints) {
+                                       if (value) {
+                                               // first assert all unique constraints
+                                               foreach (UniqueConstraint uc in this.Constraints.UniqueConstraints)
+                                               uc.AssertConstraint ();
+                                               // then assert all foreign keys
+                                               foreach (ForeignKeyConstraint fk in this.Constraints.ForeignKeyConstraints)
+                                                       fk.AssertConstraint ();
+                                       }
+                                       enforceConstraints = value;
+                               }
+                       }
+               }
+
+               internal bool RowsExist(Object[] columns, Object[] relatedColumns,DataRow row)
+               {
+                       object[] vals = new object[relatedColumns.Length];
+                       for (int i = 0; i < vals.Length; i++)
+                               vals[i] = row[(DataColumn)relatedColumns[i]];
+
+                       return RowsExist(columns,vals);
+               }
+
+               internal bool RowsExist(Object[] columns,Object[] values)
+               {
+                       bool rowsExist = false;
+                       Index indx = this.GetIndexByColumns ((DataColumn[])columns);
+
+                       if (indx != null) { // lookup for a row in index                        
+                               rowsExist = (indx.FindSimple (values, false) != null);
+                       } 
+                       
+                       if(indx == null || rowsExist == false) { 
+                               // no index or rowExist= false, we have to perform full-table scan
+                               // check that there is a parent for this row.
+                               foreach (DataRow thisRow in this.Rows) {
+                                       if (thisRow.RowState != DataRowState.Deleted) {
+                                               bool match = true;
+                                               // check if the values in the columns are equal
+                                               for (int i = 0; i < columns.Length; i++) {
+                                                       if (!thisRow[(DataColumn)columns[i]].Equals(values[i])) {
+                                                               match = false;
+                                                               break;
+                                                       }       
+                                               }
+                                               if (match) {// there is a row with columns values equals to those supplied.
+                                                       rowsExist = true;
+                                                       break;
+                                               }
+                                       }
+                               }                               
+                       }
+                       return rowsExist;
+               }
 
                /// <summary>
                /// Commits all the changes made to this table since the 
                /// last time AcceptChanges was called.
                /// </summary>
-               
-               public void AcceptChanges()
+               public void AcceptChanges () 
                {
-
                        //FIXME: Do we need to validate anything here or
                        //try to catch any errors to deal with them?
-
-                       foreach(DataRow myRow in _rows)
+                       
+                       // we do not use foreach because if one of the rows is in Delete state
+                       // it will be romeved from Rows and we get an exception.
+                       DataRow myRow;
+                       for (int i = 0; i < Rows.Count; )
                        {
+                               myRow = Rows[i];
                                myRow.AcceptChanges();
-                       }
 
+                               // if the row state is Detached it meens that it was removed from row list (Rows)
+                               // so we should not increase 'i'.
+                               if (myRow.RowState != DataRowState.Detached)
+                                       i++;
+                       }
                }
 
                /// <summary>
@@ -399,26 +600,76 @@ namespace System.Data {
                /// on a form or used by another component. The initialization
                /// occurs at runtime.
                /// </summary>
-               
-               public void BeginInit()
+               public virtual void BeginInit () 
                {
+                       _initStatus = initStatus.BeginInit;
                }
 
                /// <summary>
                /// Turns off notifications, index maintenance, and 
                /// constraints while loading data.
                /// </summary>
-               
-               public void BeginLoadData()
+               [MonoTODO]
+               public void BeginLoadData () 
                {
+                       if (!this._duringDataLoad)
+                       {
+                               //duringDataLoad is important to EndLoadData and
+                               //for not throwing unexpected exceptions.
+                               this._duringDataLoad = true;
+                               this._nullConstraintViolationDuringDataLoad = false;
+                       
+                               if (this.dataSet != null)
+                               {
+                                       //Saving old Enforce constraints state for later
+                                       //use in the EndLoadData.
+                                       this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
+                                       this.dataSet.EnforceConstraints = false;
+                               }
+                               else {
+                                       //if table does not belong to any data set use EnforceConstraints of the table
+                                       this.dataTablePrevEnforceConstraints = this.EnforceConstraints;
+                                       this.EnforceConstraints = false;
+                               }
+                       }
+                       return;
                }
 
                /// <summary>
                /// Clears the DataTable of all data.
                /// </summary>
-               
-               public void Clear()
-               {
+               public void Clear () {
+                       // TODO: thow an exception if any rows that 
+                       //       have enforced child relations 
+                       //       that would result in child rows being orphaned
+                       // now we check if any ForeignKeyConstraint is referncing 'table'.
+                       if (DataSet != null)
+                       {
+                               if (DataSet._xmlDataDocument != null) 
+                                       throw new NotSupportedException ("Clear function on dataset and datatable is not supported on XmlDataDocument.");
+
+                               IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
+                       
+                               // loop on all tables in dataset
+                               while (tableEnumerator.MoveNext())
+                               {
+                                       IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
+                                       // loop on all constrains in the current table
+                                       while (constraintEnumerator.MoveNext())
+                                       {
+                                               Object o = constraintEnumerator.Current;
+                                               // we only check ForeignKeyConstraint
+                                               if (o is ForeignKeyConstraint)
+                                               {
+                                                       ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
+                                                       if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
+                                                               throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       
                        _rows.Clear ();
                }
 
@@ -426,11 +677,10 @@ namespace System.Data {
                /// Clones the structure of the DataTable, including
                ///  all DataTable schemas and constraints.
                /// </summary>
-               
-               [MonoTODO]
-               public virtual DataTable Clone()
+               public virtual DataTable Clone () 
                {
                        DataTable Copy = new DataTable ();                      
+                       
                        CopyProperties (Copy);
                        return Copy;
                }
@@ -439,81 +689,152 @@ namespace System.Data {
                /// Computes the given expression on the current_rows that 
                /// pass the filter criteria.
                /// </summary>
-               
                [MonoTODO]
-               public object Compute(string expression, string filter)
-               {                       
-                       //FIXME: //Do a real compute
-                       object obj = "a";
+               public object Compute (string expression, string filter) 
+               {
+                       // expression is an aggregate function
+                       // filter is an expression used to limit rows
+
+                       DataRow[] rows = Select(filter);
+                       
+                       Parser parser = new Parser (rows);
+                       IExpression expr = parser.Compile (expression);
+                       object obj = expr.Eval (rows[0]);
+                       
                        return obj;
                }
 
                /// <summary>
                /// Copies both the structure and data for this DataTable.
                /// </summary>
-               [MonoTODO]      
-               public DataTable Copy()
+               public DataTable Copy () 
                {
                        DataTable Copy = new DataTable ();
-
-
                        CopyProperties (Copy);
 
+                       // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
+                       // because if the row state is deleted we get an exception.
+                       Copy._duringDataLoad = true;
                        foreach (DataRow Row in Rows) {
                                DataRow NewRow = Copy.NewRow ();
-                               NewRow.RowError = Row.RowError;
-                               foreach (DataColumn C in Copy.Columns) {
-                                       NewRow [C.ColumnName] = Row [C.ColumnName];
-                               }
                                Copy.Rows.Add (NewRow);
+                               Row.CopyValuesToRow(NewRow);
                        }
-                                       
+                       Copy._duringDataLoad = false;           
                        return Copy;
                }
 
-               [MonoTODO]
-               private void CopyProperties (DataTable Copy)
+               private void CopyProperties (DataTable Copy) 
                {
+                                       
                        Copy.CaseSensitive = CaseSensitive;
+                       Copy.VirginCaseSensitive = VirginCaseSensitive;
+
                        // Copy.ChildRelations
                        // Copy.Constraints
                        // Copy.Container
                        // Copy.DefaultView
                        // Copy.DesignMode
                        Copy.DisplayExpression = DisplayExpression;
-                       // Copy.ExtendedProperties
+                       if(ExtendedProperties.Count > 0) {
+                               //  Cannot copy extended properties directly as the property does not have a set accessor
+                               Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
+                               ExtendedProperties.Keys.CopyTo (tgtArray, 0);
+                               for (int i=0; i < ExtendedProperties.Count; i++)
+                                       Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
+                       }
                        Copy.Locale = Locale;
                        Copy.MinimumCapacity = MinimumCapacity;
                        Copy.Namespace = Namespace;
                        // Copy.ParentRelations
                        Copy.Prefix = Prefix;
-                       //Copy.PrimaryKey = PrimaryKey;
                        Copy.Site = Site;
                        Copy.TableName = TableName;
 
+
+
                        // Copy columns
                        foreach (DataColumn Column in Columns) {
-                               Copy.Columns.Add (CopyColumn (Column));                         
+                               
+                               Copy.Columns.Add (CopyColumn (Column)); 
                        }
 
+                       CopyConstraints(Copy);
+                       // add primary key to the copy
+                       if (PrimaryKey.Length > 0)
+                       {
+                               DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
+                               for (int i = 0; i < pColumns.Length; i++)
+                                       pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
+
+                               Copy.PrimaryKey = pColumns;
+                       }
+               }
+
+               private void CopyConstraints(DataTable copy)
+               {
+                       UniqueConstraint origUc;
+                       UniqueConstraint copyUc;
+                       for (int i = 0; i < this.Constraints.Count; i++)
+                       {
+                               if (this.Constraints[i] is UniqueConstraint)
+                               {
+                                       origUc = (UniqueConstraint)this.Constraints[i];
+                                       DataColumn[] columns = new DataColumn[origUc.Columns.Length];
+                                       for (int j = 0; j < columns.Length; j++)
+                                               columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
+                                       
+                                       copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
+                                       copy.Constraints.Add(copyUc);
+                               }
+                       }
                }
                /// <summary>
                /// Ends the initialization of a DataTable that is used 
                /// on a form or used by another component. The 
                /// initialization occurs at runtime.
                /// </summary>
-               
-               public void EndInit()
+               [MonoTODO]
+               public virtual void EndInit () 
                {
+                        _initStatus = initStatus.EndInit;
+                        // Add the constraints
+                        PostEndInit _postEndInit = new PostEndInit (_constraintCollection.PostEndInit);
+                        _postEndInit();
+
+
                }
 
                /// <summary>
                /// Turns on notifications, index maintenance, and 
                /// constraints after loading data.
                /// </summary>
-               
-               public void EndLoadData()
+               public void EndLoadData() 
                {
+                       int i = 0;
+                       if (this._duringDataLoad) 
+                       {
+                               
+                               if (this.dataSet !=null)
+                               {
+                                       //Getting back to previous EnforceConstraint state
+                                       this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
+                               }
+                               else {
+                                       //Getting back to the table's previous EnforceConstraint state
+                                       this.EnforceConstraints = this.dataTablePrevEnforceConstraints;
+                               }
+
+                               if(this._nullConstraintViolationDuringDataLoad) {
+                                       this._nullConstraintViolationDuringDataLoad = false;
+                                       throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
+                               }
+
+                               //Returning from loading mode, raising exceptions as usual
+                               this._duringDataLoad = false;
+
+                       }
+
                }
 
                /// <summary>
@@ -521,11 +842,9 @@ namespace System.Data {
                ///  changes made to it since it was loaded or 
                ///  AcceptChanges was last called.
                /// </summary>
-               [MonoTODO]
-               public DataTable GetChanges()
+               public DataTable GetChanges() 
                {
-                       //TODO:
-                       return this;
+                       return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
                }
 
                /// <summary>
@@ -533,53 +852,75 @@ namespace System.Data {
                /// changes made to it since it was last loaded, or 
                /// since AcceptChanges was called, filtered by DataRowState.
                /// </summary>
-               [MonoTODO]      
-               public DataTable GetChanges(DataRowState rowStates)
+               public DataTable GetChanges(DataRowState rowStates) 
                {
-                       //TODO:
-                       return this;
+                       DataTable copyTable = null;
+
+                       IEnumerator rowEnumerator = Rows.GetEnumerator();
+                       while (rowEnumerator.MoveNext()) {
+                               DataRow row = (DataRow)rowEnumerator.Current;
+                               // The spec says relationship constraints may cause Unchanged parent rows to be included but
+                               // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
+                               if (row.IsRowChanged(rowStates)) {
+                                       if (copyTable == null)
+                                               copyTable = Clone();
+                                       DataRow newRow = copyTable.NewRow();
+                                       row.CopyValuesToRow(newRow);
+                                       copyTable.Rows.Add (newRow);
+                               }
+                       }
+                        
+                       return copyTable;
                }
 
-               /// <summary>
-               /// Gets an array of DataRow objects that contain errors.
-               /// </summary>
-               
+#if NET_2_0
                [MonoTODO]
-               public DataRow[] GetErrors()
+               public DataTableReader GetDataReader ()
                {
                        throw new NotImplementedException ();
                }
+#endif
 
                /// <summary>
-               /// This member supports the .NET Framework infrastructure
-               /// and is not intended to be used directly from your code.
+               /// Gets an array of DataRow objects that contain errors.
                /// </summary>
-               
-               protected virtual DataTable CreateInstance()
+               public DataRow[] GetErrors () 
                {
-                        return Activator.CreateInstance(this.GetType(), true) as DataTable;
+                       ArrayList errors = new ArrayList();
+                       for (int i = 0; i < _rows.Count; i++)
+                       {
+                               if (_rows[i].HasErrors)
+                                       errors.Add(_rows[i]);
+                       }
+                       
+                       return (DataRow[]) errors.ToArray(typeof(DataRow));
+               }
+       
+               /// <summary>
+               /// This member is only meant to support Mono's infrastructure 
+               /// </summary>
+               protected virtual DataTable CreateInstance () 
+               {
+                       return Activator.CreateInstance (this.GetType (), true) as DataTable;
                }
 
                /// <summary>
-               /// This member supports the .NET Framework infrastructure
-               /// and is not intended to be used directly from your code.
+               /// This member is only meant to support Mono's infrastructure 
                /// </summary>
-               
-               protected virtual Type GetRowType()
+               protected virtual Type GetRowType () 
                {
-                        return typeof (DataRow);
+                       return typeof (DataRow);
                }
 
                /// <summary>
-               /// This member supports the .NET Framework infrastructure 
-               /// and is not intended to be used directly from your code.
+               /// This member is only meant to support Mono's infrastructure 
                /// 
                /// Used for Data Binding between System.Web.UI. controls 
                /// like a DataGrid
                /// or
                /// System.Windows.Forms controls like a DataGrid
                /// </summary>
-               IList IListSource.GetList()
+               IList IListSource.GetList () 
                {
                        IList list = (IList) _defaultView;
                        return list;
@@ -589,27 +930,62 @@ namespace System.Data {
                /// Copies a DataRow into a DataTable, preserving any 
                /// property settings, as well as original and current values.
                /// </summary>
-               [MonoTODO]
-               public void ImportRow(DataRow row)
+               public void ImportRow (DataRow row) 
                {
+                       DataRow newRow = NewRow();
+                       Rows.Add(newRow);
+                       row.CopyValuesToRow(newRow);
+                       
                }
 
                /// <summary>
-               /// This member supports the .NET Framework infrastructure
-               ///  and is not intended to be used directly from your code.
+               /// This member is only meant to support Mono's infrastructure          
                /// </summary>
-               
+               void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
+               {
+                       DataSet dset;
+                       if (dataSet != null)
+                               dset = dataSet;
+                       else {
+                               dset = new DataSet ("tmpDataSet");
+                               dset.Tables.Add (this);
+                       }
+                       
+                       StringWriter sw = new StringWriter ();
+                       XmlTextWriter tw = new XmlTextWriter (sw);
+                       dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
+                       tw.Close ();
+                       
+                       StringWriter sw2 = new StringWriter ();
+                       DataTableCollection tables = new DataTableCollection (dset);
+                       tables.Add (this);
+                       XmlSchema schema = dset.BuildSchema (tables, null);
+                       schema.Write (sw2);
+                       sw2.Close ();
+                       
+                       info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
+                       info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
+               }
+
+#if NET_2_0
                [MonoTODO]
-               void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+               public void Load (IDataReader reader)
                {
+                       throw new NotImplementedException ();
                }
 
+               [MonoTODO]
+               public void Load (IDataReader reader, LoadOption loadOption)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+
                /// <summary>
                /// Finds and updates a specific row. If no matching row
                ///  is found, a new row is created using the given values.
                /// </summary>
-               [MonoTODO]
-               public DataRow LoadDataRow(object[] values, bool fAcceptChanges)
+               public DataRow LoadDataRow (object[] values, bool fAcceptChanges) 
                {
                        DataRow row = null;
                        if (PrimaryKey.Length == 0) {
@@ -617,52 +993,134 @@ namespace System.Data {
                                if (fAcceptChanges)
                                        row.AcceptChanges ();
                        }
-                       else
-                               throw new NotImplementedException ();
+                       else {
+                               bool hasPrimaryValues = true;
+                               // initiate an array that has the values of the primary keys.
+                               object[] keyValues = new object[PrimaryKey.Length];
+                               for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
+                               {
+                                       if(PrimaryKey[i].Ordinal < values.Length)
+                                               keyValues[i] = values[PrimaryKey[i].Ordinal];
+                                       else
+                                               hasPrimaryValues = false;
+                               }
+                               
+                               if (hasPrimaryValues){
+                                       // find the row in the table.
+                                       row = Rows.Find(keyValues);
+                               }
+                               
+                               if (row == null)
+                                       row = Rows.Add (values);
+                               else
+                                       row.ItemArray = values;
+                               
+                               if (fAcceptChanges)
+                                       row.AcceptChanges ();
+                       }
+                               
                        return row;
                }
 
+#if NET_2_0
+               [MonoTODO]
+               public DataRow LoadDataRow (object[] values, LoadOption loadOption)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void Merge (DataTable table)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void Merge (DataTable table, bool preserveChanges)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+
                /// <summary>
                /// Creates a new DataRow with the same schema as the table.
                /// </summary>
-               public DataRow NewRow()
+               public DataRow NewRow () 
                {
-                       return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
+                       // initiate only one row builder.
+                       if (_rowBuilder == null)
+                               _rowBuilder = new DataRowBuilder (this, 0, 0);
+                       
+                       // new row get id -1.
+                       _rowBuilder._rowId = -1;
+                       return this.NewRowFromBuilder (_rowBuilder);
                }
 
                /// <summary>
                /// This member supports the .NET Framework infrastructure
                ///  and is not intended to be used directly from your code.
                /// </summary>
-               [MonoTODO]
-               protected internal DataRow[] NewRowArray(int size)
+               protected internal DataRow[] NewRowArray (int size) 
                {
-                       DataRow[] dataRows = {null};
-                       return dataRows;
+                       return (DataRow[]) Array.CreateInstance (GetRowType (), size);
                }
 
                /// <summary>
                /// Creates a new row from an existing row.
                /// </summary>
-               
-               protected virtual DataRow NewRowFromBuilder(DataRowBuilder builder)
+               protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) 
                {
                        return new DataRow (builder);
                }
-       
+               
+               
+
+#if NET_2_0
+               [MonoTODO]
+               XmlReadMode ReadXml (Stream stream)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public void ReadXmlSchema (Stream stream)
+               {
+                       XmlSchemaMapper mapper = new XmlSchemaMapper (this);
+                       mapper.Read (new XmlTextReader(stream));
+               }
+
+               public void ReadXmlSchema (TextReader reader)
+               {
+                       XmlSchemaMapper mapper = new XmlSchemaMapper (this);
+                       mapper.Read (new XmlTextReader(reader));
+               }
+
+               public void ReadXmlSchema (string fileName)
+               {
+                       StreamReader reader = new StreamReader (fileName);
+                       ReadXmlSchema (reader);
+                       reader.Close ();
+               }
+
+               public void ReadXmlSchema (XmlReader reader)
+               {
+                       XmlSchemaMapper mapper = new XmlSchemaMapper (this);
+                       mapper.Read (reader);
+               }
+#endif
 
                /// <summary>
                /// Rolls back all changes that have been made to the 
                /// table since it was loaded, or the last time AcceptChanges
                ///  was called.
                /// </summary>
-               
-               [MonoTODO]
-               public void RejectChanges()
-               {
-                       
-                       //foreach(DataRow myRow in _rows)
-                       //{
+               public void RejectChanges () 
+               {       
                        for (int i = _rows.Count - 1; i >= 0; i--) {
                                DataRow row = _rows [i];
                                if (row.RowState != DataRowState.Unchanged)
@@ -672,23 +1130,31 @@ namespace System.Data {
 
                /// <summary>
                /// Resets the DataTable to its original state.
-               /// </summary>
-               
-               [MonoTODO]
-               public virtual void Reset()
+               /// </summary>          
+               public virtual void Reset () 
                {
+                       Clear();
+                       while (ParentRelations.Count > 0)
+                       {
+                               if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
+                                       dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
+                       }
+
+                       while (ChildRelations.Count > 0)
+                       {
+                               if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
+                                       dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
+                       }
+                       Constraints.Clear();
+                       Columns.Clear();
                }
 
                /// <summary>
                /// Gets an array of all DataRow objects.
                /// </summary>
-               
-               [MonoTODO]
-               public DataRow[] Select()
+               public DataRow[] Select () 
                {
-                       //FIXME:
-                       DataRow[] dataRows = {null};
-                       return dataRows;
+                       return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
                }
 
                /// <summary>
@@ -696,20 +1162,9 @@ namespace System.Data {
                /// the filter criteria in order of primary key (or 
                /// lacking one, order of addition.)
                /// </summary>
-               
-               [MonoTODO]
-               public DataRow[] Select(string filterExpression)
+               public DataRow[] Select (string filterExpression) 
                {
-                       ExpressionElement Expression = new ExpressionMainElement (filterExpression);
-
-                       ArrayList List = new ArrayList ();
-                       foreach (DataRow Row in Rows) {
-                               
-                               if (Expression.Test (Row))
-                                       List.Add (Row);
-                       }
-                       
-                       return (DataRow [])List.ToArray (typeof (DataRow));
+                       return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
                }
 
                /// <summary>
@@ -717,11 +1172,9 @@ namespace System.Data {
                /// match the filter criteria, in the the 
                /// specified sort order.
                /// </summary>
-               [MonoTODO]
-               public DataRow[] Select(string filterExpression, string sort)
+               public DataRow[] Select (string filterExpression, string sort) 
                {
-                       DataRow[] dataRows = {null};
-                       return dataRows;
+                       return Select(filterExpression, sort, DataViewRowState.CurrentRows);
                }
 
                /// <summary>
@@ -730,45 +1183,249 @@ namespace System.Data {
                /// the specified state.
                /// </summary>
                [MonoTODO]
-               public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
+               public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) 
                {
-                       DataRow[] dataRows = {null};
+                       if (filterExpression == null)
+                               filterExpression = String.Empty;
+
+                       IExpression filter = null;
+                       if (filterExpression != String.Empty) {
+                               Parser parser = new Parser ();
+                               filter = parser.Compile (filterExpression);
+                       }
+
+                       ArrayList rowList = new ArrayList();
+                       int recordStateFilter = GetRowStateFilter(recordStates);
+                       foreach (DataRow row in Rows) {
+                               if (((int)row.RowState & recordStateFilter) != 0) {
+                                       if (filter != null && !(bool)filter.Eval (row))
+                                               continue;
+                                       rowList.Add (row);
+                               }
+                       }
+
+                       DataRow[] dataRows = (DataRow[])rowList.ToArray(typeof(DataRow));
+
+                       if (sort != null && !sort.Equals(String.Empty)) 
+                       {
+                               SortableColumn[] sortableColumns = null;
+
+                               sortableColumns = ParseTheSortString (sort);
+                               if (sortableColumns == null)
+                                       throw new Exception ("sort expression result is null");
+                               if (sortableColumns.Length == 0)
+                                       throw new Exception("sort expression result is 0");
+
+                               RowSorter rowSorter = new RowSorter (this, dataRows, sortableColumns);
+                               dataRows = rowSorter.SortRows ();
+
+                               sortableColumns = null;
+                               rowSorter = null;
+                       }
+
+                       
                        return dataRows;
                }
 
+               internal void AddIndex (Index index)
+               {
+                       if (_indexes == null)
+                               _indexes = new ArrayList();
+
+                       _indexes.Add (index);
+               }
+
+               internal void RemoveIndex (Index indx)
+               {
+                       _indexes.Remove (indx);
+               }
+
+               internal Index GetIndexByColumns (DataColumn[] columns)
+               {
+                       return GetIndexByColumns(columns,false,false);
+               }
+
+//             internal Index GetIndexByColumnsExtended(DataColumn[] columns)
+//             {
+//                     DataColumn[] pkColumns = this.PrimaryKey;
+//                     if((pkColumns != null) && (pkColumns.Length > 0)) {
+//                             DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];                                  
+//                             Array.Copy(columns,0,cols,0,columns.Length);
+//                             Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
+//
+//                             return _getIndexByColumns(cols,false,false);
+//                     } else {
+//                             return null;
+//                     }
+//             }
+
+               internal Index GetIndexByColumns (DataColumn[] columns, bool unique)
+               {
+                       return GetIndexByColumns(columns,unique,true);
+               }
+
+//             internal Index GetIndexByColumnsExtended(DataColumn[] columns, bool unique)
+//             {
+//                     DataColumn[] pkColumns = this.PrimaryKey;
+//                     if((pkColumns != null) && (pkColumns.Length > 0)) {
+//                             DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];                                  
+//                             Array.Copy(columns,0,cols,0,columns.Length);
+//                             Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
+//
+//                             return _getIndexByColumns(cols,unique,true);
+//                     } else  {
+//                             return null;
+//                     }
+//             }
+
+               internal Index GetIndexByColumns(DataColumn[] columns, bool unique, bool useUnique)
+               {
+                       if (_indexes != null) {
+                               foreach (Index indx in _indexes) {
+                                       bool found = false;
+                                       if ((!useUnique) || ((useUnique)&& (indx.IsUnique))) {
+                                               found = DataColumn.AreColumnSetsTheSame (indx.Columns, columns);
+                                       }
+                                       if (found)
+                                               return indx;
+                               }
+                       }
+
+                       return null;
+               }
+
+               internal void DeleteRowFromIndexes (DataRow row)
+               {
+                       if (_indexes != null) {
+                               foreach (Index indx in _indexes) {
+                                       indx.Delete (row);
+                               }
+                       }
+               }
+
+               private static int GetRowStateFilter(DataViewRowState recordStates)
+               {
+                       int flag = 0;
+
+                       if ((recordStates & DataViewRowState.Added) != 0)
+                               flag |= (int)DataRowState.Added;
+                       if ((recordStates & DataViewRowState.Deleted) != 0)
+                               flag |= (int)DataRowState.Deleted;
+                       if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
+                               flag |= (int)DataRowState.Modified;
+                       if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
+                               flag |= (int)DataRowState.Modified;
+                       if ((recordStates & DataViewRowState.Unchanged) != 0)
+                               flag |= (int)DataRowState.Unchanged;
+
+                       return flag;
+               }
+
                /// <summary>
                /// Gets the TableName and DisplayExpression, if 
                /// there is one as a concatenated string.
                /// </summary>
-               public override string ToString()
+               public override string ToString() 
                {
                        //LAMESPEC: spec says concat the two. impl puts a 
                        //plus sign infront of DisplayExpression
-                       return TableName + " " + DisplayExpression;
+                       string retVal = TableName;
+                       if(DisplayExpression != null && DisplayExpression != "")
+                               retVal += " + " + DisplayExpression;
+                       return retVal;
+               }
+
+#if NET_2_0
+               [MonoTODO]
+               public void WriteXml (Stream stream)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXml (TextWriter writer)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXml (XmlWriter writer)
+               {
+                       throw new NotImplementedException ();
                }
 
+               [MonoTODO]
+               public void WriteXml (string fileName)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXml (Stream stream, XmlWriteMode mode)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXml (TextWriter writer, XmlWriteMode mode)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXml (XmlWriter writer, XmlWriteMode mode)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXml (string fileName, XmlWriteMode mode)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXmlSchema (Stream stream)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXmlSchema (TextWriter writer)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXmlSchema (XmlWriter writer)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               public void WriteXmlSchema (string fileName)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
                
                #region Events /////////////////
                
                /// <summary>
                /// Raises the ColumnChanged event.
                /// </summary>
-               protected virtual void OnColumnChanged(DataColumnChangeEventArgs e)
-               {
-                       if (null != ColumnChanged)
-                       {
-                               ColumnChanged(this, e);
+               protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
+                       if (null != ColumnChanged) {
+                               ColumnChanged (this, e);
                        }
                }
 
                /// <summary>
                /// Raises the ColumnChanging event.
                /// </summary>
-               protected virtual void OnColumnChanging(DataColumnChangeEventArgs e)
-               {
-                       if (null != ColumnChanging)
-                       {
-                               ColumnChanging(this, e);
+               protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
+                       if (null != ColumnChanging) {
+                               ColumnChanging (this, e);
                        }
                }
 
@@ -776,34 +1433,30 @@ namespace System.Data {
                /// Raises the PropertyChanging event.
                /// </summary>
                [MonoTODO]
-               protected internal virtual void OnPropertyChanging(PropertyChangedEventArgs pcevent)
-               {
-//                     if (null != PropertyChanging)
-//                     {
-//                             PropertyChanging(this, e);
-//                     }
+               protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
+                       //      if (null != PropertyChanging)
+                       //      {
+                       //              PropertyChanging (this, e);
+                       //      }
                }
 
                /// <summary>
                /// Notifies the DataTable that a DataColumn is being removed.
                /// </summary>
                [MonoTODO]
-               protected internal virtual void OnRemoveColumn(DataColumn column)
-               {
-//                     if (null != RemoveColumn)
-//                     {
-//                             RemoveColumn(this, e);
-//                     }
+               protected internal virtual void OnRemoveColumn (DataColumn column) {
+                       //      if (null != RemoveColumn)
+                       //      {
+                       //              RemoveColumn(this, e);
+                       //      }
+                       this.Rows.onColumnRemoved(column.Ordinal);
                }
 
                /// <summary>
                /// Raises the RowChanged event.
                /// </summary>
-               
-               protected virtual void OnRowChanged(DataRowChangeEventArgs e)
-               {
-                       if (null != RowChanged)
-                       {
+               protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
+                       if (null != RowChanged) {
                                RowChanged(this, e);
                        }
                }
@@ -812,11 +1465,8 @@ namespace System.Data {
                /// <summary>
                /// Raises the RowChanging event.
                /// </summary>
-               
-               protected virtual void OnRowChanging(DataRowChangeEventArgs e)
-               {
-                       if (null != RowChanging)
-                       {
+               protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
+                       if (null != RowChanging) {
                                RowChanging(this, e);
                        }
                }
@@ -824,10 +1474,8 @@ namespace System.Data {
                /// <summary>
                /// Raises the RowDeleted event.
                /// </summary>
-               protected virtual void OnRowDeleted(DataRowChangeEventArgs e)
-               {
-                       if (null != RowDeleted)
-                       {
+               protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
+                       if (null != RowDeleted) {
                                RowDeleted(this, e);
                        }
                }
@@ -835,17 +1483,14 @@ namespace System.Data {
                /// <summary>
                /// Raises the RowDeleting event.
                /// </summary>
-               protected virtual void OnRowDeleting(DataRowChangeEventArgs e)
-               {
-                       if (null != RowDeleting)
-                       {
+               protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
+                       if (null != RowDeleting) {
                                RowDeleting(this, e);
                        }
                }
 
                [MonoTODO]
-               private DataColumn CopyColumn (DataColumn Column)
-               {
+               private DataColumn CopyColumn (DataColumn Column) {
                        DataColumn Copy = new DataColumn ();
 
                        // Copy all the properties of column
@@ -856,9 +1501,9 @@ namespace System.Data {
                        Copy.Caption = Column.Caption;
                        Copy.ColumnMapping = Column.ColumnMapping;
                        Copy.ColumnName = Column.ColumnName;
-                       // Copy.Container
-                       // Copy.DataType
-                       // Copy.DefaultValue                    
+                       //Copy.Container
+                       Copy.DataType = Column.DataType;
+                       Copy.DefaultValue = Column.DefaultValue;                        
                        Copy.Expression = Column.Expression;
                        //Copy.ExtendedProperties
                        Copy.MaxLength = Column.MaxLength;
@@ -866,7 +1511,8 @@ namespace System.Data {
                        Copy.Prefix = Column.Prefix;
                        Copy.ReadOnly = Column.ReadOnly;
                        //Copy.Site
-                       Copy.Unique = Column.Unique;
+                       //we do not copy the unique value - it will be copyied when copying the constraints.
+                       //Copy.Unique = Column.Unique;
                        
                        return Copy;
                }                       
@@ -915,7 +1561,287 @@ namespace System.Data {
                [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
                public event DataRowChangeEventHandler RowDeleting;
                
-               #endregion //Events
-       }
+               #endregion // Events
 
+               /// <summary>
+               ///  Removes all UniqueConstraints
+               /// </summary>
+               private void RemoveUniqueConstraints () 
+               {
+                       foreach (Constraint Cons in Constraints) {
+                               
+                               if (Cons is UniqueConstraint) {
+                                       Constraints.Remove (Cons);
+                                       break;
+                               }
+                       }
+                       
+                       UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
+               }
+
+               // to parse the sort string for DataTable:Select(expression,sort)
+               // into sortable columns (think ORDER BY, 
+               // such as, "customer ASC, price DESC" )
+               private SortableColumn[] ParseTheSortString (string sort) 
+               {
+                       SortableColumn[] sortColumns = null;
+                       ArrayList columns = null;
+               
+                       if (sort != null && !sort.Equals ("")) {
+                               columns = new ArrayList ();
+                               string[] columnExpression = sort.Trim ().Split (new char[1] {','});
+                       
+                               for (int c = 0; c < columnExpression.Length; c++) {
+                                       string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
+                               
+                                       string columnName = columnSortInfo[0].Trim ();
+                                       string sortOrder = "ASC";
+                                       if (columnSortInfo.Length > 1) 
+                                               sortOrder = columnSortInfo[1].Trim ().ToUpper (Locale);
+                                       
+                                       ListSortDirection sortDirection = ListSortDirection.Ascending;
+                                       switch (sortOrder) {
+                                       case "ASC":
+                                               sortDirection = ListSortDirection.Ascending;
+                                               break;
+                                       case "DESC":
+                                               sortDirection = ListSortDirection.Descending;
+                                               break;
+                                       default:
+                                               throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
+                                       }
+                                       Int32 ord = 0;
+                                       try {
+                                               ord = Int32.Parse (columnName);
+                                       }
+                                       catch (FormatException) {
+                                               ord = -1;
+                                       }
+                                       DataColumn dc = null;
+                                       if (ord == -1)                          
+                                               dc = _columnCollection[columnName];
+                                       else
+                                               dc = _columnCollection[ord];
+                                       SortableColumn sortCol = new SortableColumn (dc,sortDirection);
+                                       columns.Add (sortCol);
+                               }       
+                               sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
+                       }               
+                       return sortColumns;
+               }
+       
+               private class SortableColumn 
+               {
+                       private DataColumn col;
+                       private ListSortDirection dir;
+
+                       internal SortableColumn (DataColumn column, 
+                                               ListSortDirection direction) 
+                       {
+                               col = column;
+                               dir = direction;
+                       }
+
+                       public DataColumn Column {
+                               get {
+                                       return col;
+                               }
+                       }
+
+                       public ListSortDirection SortDirection {
+                               get {
+                                       return dir;
+                               }
+                       }
+               }
+
+               private class RowSorter : IComparer 
+               {
+                       private DataTable table;
+                       private SortableColumn[] sortColumns;
+                       private DataRow[] rowsToSort;
+                       
+                       internal RowSorter(DataTable table,
+                                       DataRow[] unsortedRows, 
+                                       SortableColumn[] sortColumns) 
+                       {
+                               this.table = table;
+                               this.sortColumns = sortColumns;
+                               this.rowsToSort = unsortedRows;
+                       }
+
+                       public SortableColumn[] SortColumns {
+                               get {
+                                       return sortColumns;
+                               }
+                       }
+                       
+                       public DataRow[] SortRows () 
+                       {
+                               Array.Sort (rowsToSort, this);
+                               return rowsToSort;
+                       }
+
+                       int IComparer.Compare (object x, object y) 
+                       {
+                               if(x == null)
+                                       throw new Exception ("Object to compare is null: x");
+                               if(y == null)
+                                       throw new Exception ("Object to compare is null: y");
+                               if(!(x is DataRow))
+                                       throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
+                               if(!(y is DataRow))
+                                       throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
+
+                               DataRow rowx = (DataRow) x;
+                               DataRow rowy = (DataRow) y;
+
+                               for(int i = 0; i < sortColumns.Length; i++) {
+                                       SortableColumn sortColumn = sortColumns[i];
+                                       DataColumn dc = sortColumn.Column;
+
+                                       IComparable objx = (IComparable) rowx[dc];
+                                       object objy = rowy[dc];
+
+                                       int result = CompareObjects (objx, objy);
+                                       if (result != 0) {
+                                               if (sortColumn.SortDirection == ListSortDirection.Ascending) {
+                                                       return result;
+                                               }
+                                               else {
+                                                       return -result;
+                                               }
+                                       }
+                               }
+                               return 0;
+                       }
+
+                       private int CompareObjects (object a, object b) 
+                       {
+                               if (a == b)
+                                       return 0;
+                               else if (a == null)
+                                       return -1;
+                               else if (a == DBNull.Value)
+                                       return -1;
+                               else if (b == null)
+                                       return 1;
+                               else if (b == DBNull.Value)
+                                       return 1;
+
+                               if((a is string) && (b is string)) {
+                                       a = ((string) a).ToUpper (table.Locale);
+                                       b = ((string) b).ToUpper (table.Locale);                        
+                               }
+
+                               if (a is IComparable)
+                                       return ((a as IComparable).CompareTo (b));
+                               else if (b is IComparable)
+                                       return -((b as IComparable).CompareTo (a));
+
+                               throw new ArgumentException ("Neither a nor b IComparable");
+                       }
+               }
+
+               /// <summary>
+               /// Creates new index for a table
+               /// </summary>
+               internal Index CreateIndex(string name, DataColumn[] columns, bool unique)
+               {
+                       // first check whenever index exists on the columns
+                       Index idx = this.GetIndexByColumns(columns);
+                       if(idx != null) {
+                       // if index on this columns already exists - return it
+                               return idx;
+                       }
+
+                       // create new index
+                       Index newIndex = new Index(name,this,columns,unique);
+
+                       //InitializeIndex (newIndex);                   
+
+                       // add new index to table indexes
+                       this.AddIndex(newIndex);
+                       return newIndex;
+               }
+
+//             /// <summary>
+//             /// Creates new extended index for a table
+//             /// </summary>
+//             internal Index CreateIndexExtended(string name, DataColumn[] columns, bool unique)
+//             {
+//                     // first check whenever extended index exists on the columns
+//                     Index idx = this.GetIndexByColumnsExtended(columns);
+//                     if(idx != null) {
+//                             // if extended index on this columns already exists - return it
+//                             return idx;
+//                     }
+//
+//                     DataColumn[] pkColumns = this.PrimaryKey;
+//                     if((pkColumns != null) && (pkColumns.Length > 0)) {
+//                             DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];                                  
+//                             Array.Copy(columns,0,cols,0,columns.Length);
+//                             Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
+//                             return this.CreateIndex(name, cols, unique);
+//                     } 
+//                     else {
+//                             throw new InvalidOperationException("Can not create extended index if the primary key is null or primary key does not contains any row");
+//                     }
+//             }
+
+//             /// <summary>
+//             /// Drops extended index if it is not referenced anymore
+//             /// by any of table constraints
+//             /// </summary>
+//             internal void DropIndexExtended(DataColumn[] columns)
+//             {
+//                     // first check whenever extended index exists on the columns
+//                     Index index = this.GetIndexByColumnsExtended(columns);
+//                     if(index == null) {
+//                             // if no extended index on this columns exists - do nothing
+//                             return;
+//                     }
+//                     this.DropIndex(index);
+//             }
+
+               /// <summary>
+               /// Drops index specified by columns if it is not referenced anymore
+               /// by any of table constraints
+               /// </summary>
+               internal void DropIndex(DataColumn[] columns)
+               {
+                       // first check whenever index exists for the columns
+                       Index index = this.GetIndexByColumns(columns);
+                       if(index == null) {
+                       // if no index on this columns already exists - do nothing
+                               return;
+                       }
+                       this.DropIndex(index);
+               }
+
+               internal void DropIndex(Index index)
+               {
+                       // loop through table constraints and checks 
+                       foreach(Constraint constraint in Constraints) {
+                               // if we found another reference to the index we do not remove the index.
+                               if (index == constraint.Index)
+                                       return; 
+                       }
+                       
+                       this.RemoveIndex(index);
+               }
+
+               internal void InitializeIndex (Index indx)
+               {
+                       DataRow[] rows = new DataRow[this.Rows.Count];
+                       this.Rows.CopyTo (rows, 0);
+                       indx.Root = null;
+                       // fill index with table rows
+                       foreach(DataRow row in this.Rows) {
+                               if(row.RowState != DataRowState.Deleted) {
+                                       indx.Insert(new Node(row), DataRowVersion.Default);
+                               }
+                       }
+               }
+       }
 }