/*******************************************************}
{                                                       }
{               Borland DB Web                          }
{           Data aware Web controls                     }
{  Copyright (c) 2003 Borland Software Corporation      }
{                                                       }
{*******************************************************/

using System;
using System.Data;
using System.IO;
using System.Text;                             
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Borland.Data.Web
{
   #region PageStateManager
   public class PageStateManager
   {
	private const int MaxTablesInDataSet = 999999;
   	private Page page;
      private DBWebDataSource dataSource;
      private IDBDataSource idataSource;
      private IDBPageStateManager dataSourceAccess;
      // last row here means the row that was current prior to
      // current scrolling, and does NOT mean the last row of
      // the table.  The last row of the table is RowCount!
      private int [] FLastRow;
      private int [] FRowCount;
   	// Current physical row position for all tables in dataset unadjusted for
      // deleted or inserted rows.
      private int [] FCurrentRow;
      // Keep track of deleted records not sent to server for undo purposes
      private int[][] FDeletedRows;
      private int[][] FInsertedRows;
      // store current row when DataSet is not yet available
      private int FCurrentRowHolder;
      // These contain values set with RegisterHidden Fields as
      // well as any values from SUBMIT field on page
      private ArrayList FErrors;
      private ArrayList FWarnings;
      private ArrayList FDuplicateColumns;
		private NameValueCollection postCollectionValues;
      public ClientAction clientAction;
      private int FRowChangeController;
	  private bool [] FDatasetUpdated;
      private bool [] FHiddenRowsSet;
      private bool [] FPostCollectionValuesSet;
      // Asp Grid ID -- needed to tell when a Grid control has been found.
      private string FAspGridId;


   	public PageStateManager(Page p, DBWebDataSource ds, string gridIdentifier)
      {
      	page = p;
         dataSource = ds;
         dataSourceAccess = ds as IDBPageStateManager;
         idataSource = (ds as IDBDataSource);
		 FCurrentRow = null;
         clientAction = ClientAction.ecaNone;
         postCollectionValues = new NameValueCollection();
         FCurrentRowHolder = 0;
         FRowChangeController = MaxTablesInDataSet;
         FErrors = new ArrayList();
         FWarnings = new ArrayList();
         FDuplicateColumns = new ArrayList();
         if( !ClassUtils.IsDesignTime(page) && !ds.AutoRefresh)
         	if( page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] == null && ds.DataSource != null )
         		page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] = ds.DataSource;
		 FAspGridId = gridIdentifier;
	  }

	  public Page GetPage()
	  {
	  	return page;
	  }

      public bool CheckPage(Page p)
      {
      	return page == p;
      }

      public ArrayList Errors
      {
      	get
         {
         	return FErrors;
         }
      }
      public ArrayList Warnings
      {
      	get
         {
         	return FWarnings;
         }
      }


      #region finding dirty records: insertions/deletions


      public bool ForceUpdates(string TableName)
      {
      	return GetDeleteCount(TableName, -1) > 0 || GetInsertCount(TableName, -1) > 0;
      }
      
      protected int ChangedRowCount( string TableName, int iToRow )
      {
      	return GetInsertCount(TableName, iToRow) - GetDeleteCount(TableName, iToRow);
      }

      // any time we check for RowCount, we need to adjust count by
      // the # of deletions, since the table will continue holding deleted
      // rows.
		public int GetInsertCount(string TableName, int iToRow)
		{
         if( ClassUtils.IsDesignTime(page) )
         	return 0;
      	if( FInsertedRows == null )
         	SetupCurrentRow();
         int index = dataSource.IndexOfTable(TableName);
         if( FInsertedRows[index] == null )
         	return 0;
         if( iToRow == -1 )
         	return FInsertedRows[index].Length;
         int iCount = 0;
         for( int i = 0; i < FDeletedRows[index].Length; i++ )
         {
         	if(FInsertedRows[index][i] < iToRow)
            	iCount++;
         }
         return iCount;
		}

      public bool GetPageFieldsWritten(string TableName)
      {
      	bool bWritten = false;
         int index = dataSource.IndexOfTable(TableName);
         if( index >= 0 )
         {
      		bWritten = HiddenRowsSet[index];
            FHiddenRowsSet[index] = true;
         }
         return bWritten;
      }

      public bool GetDatasetUpdated(string TableName)
      {
         int index = dataSource.IndexOfTable(TableName);
         if( index >= 0 )
      		return DatasetUpdated[index];
         return false;
      }

      public bool GetPostCollectionValuesSet(string TableName)
      {
         int index = dataSource.IndexOfTable(TableName);
         if( index >= 0 )
      		return PostCollectionValuesSet[index];
         return false;
      }

      public void SetDatasetUpdated(string TableName, bool value)
      {
         int index = dataSource.IndexOfTable(TableName);
         if( index >= 0 )
      		DatasetUpdated[index] = value;
      }

		protected int AddDeletedToRowCount( string TableName, int iToRow )
      {
         int iStart = 0;
         int iTotal = iToRow;
         int iCount = GetRowCount(TableName);
         for( int i = 0; i < iCount; i++ )
         {
         	if( !IsRowDeleted(TableName, i) )
            	iStart++;
            else
               iTotal++;
            if( iStart > iToRow )
            	break;
         }
         return iTotal;
      }
		// if iRow == -1, get count of all deleted rows
		// if iRow == valid row, only get count of deleted
		//     rows prior to iRow
		public int GetDeleteCount( string TableName, int iToRow )
		{
         int iCount = 0;
         if( ClassUtils.IsDesignTime(page) )
         	return iCount;
         int index = dataSource.IndexOfTable(TableName);
         if( FDeletedRows[index] == null )
         	return iCount;
         if( iToRow == -1 )
         	return FDeletedRows[index].Length;
         for( int i = 0; i < FDeletedRows[index].Length; i++ )
         {
         	if(FDeletedRows[index][i] < iToRow)
            	iCount++;
         }
         return iCount;
		}
      #endregion finding dirty records: insertions/deletions

      #region row state access functions

      public int [] CurrentRow
      {
      	get
         {
         	if( FCurrentRow == null )
            	SetupCurrentRow();
			return FCurrentRow;
         }
      }

      public int [] RowCount
      {
      	get
         {
         	if( FRowCount == null )
            	SetupCurrentRow();
            return FRowCount;
         }
      }

      public int [] GetDeleteRows(string TableName)
      {
      	if( FDeletedRows == null )
         	SetupCurrentRow();
         int index = dataSource.IndexOfTable(TableName);
         return FDeletedRows[index];
      }

      public bool IsRowDeleted(string TableName, int iRow)
      {
      	if( FDeletedRows == null )
         	SetupCurrentRow();
         int index = dataSource.IndexOfTable(TableName);
         if( FDeletedRows[index] == null )
         	return false;
         for( int i = 0; i < FDeletedRows[index].Length; i++ )
         	if(FDeletedRows[index][i] == iRow)
            	return true;
         return false;
      }

      public bool IsRowInserted(string TableName, int iRow)
      {
      	if( FInsertedRows == null )
         	SetupCurrentRow();
         int index = dataSource.IndexOfTable(TableName);
         if( FInsertedRows[index] == null )
         	return false;
         for( int i = 0; i < FInsertedRows[index].Length; i++ )
         	if(FInsertedRows[index][i] == iRow)
            	return true;
         return false;
      }

      protected void AddDeletedRow(string TableName, int value)
      {
      	if( FDeletedRows == null )
         	SetupCurrentRow();
         int index = dataSource.IndexOfTable(TableName);
         if( FDeletedRows[index] == null )
         {
         	FDeletedRows[index] = new int[1];
            FDeletedRows[index][0] = value;
         }
         else
         {
         	int [] temp = new int [FDeletedRows[index].Length + 1];
            for( int i = 0; i < FDeletedRows[index].Length; i++ )
            	temp[i] = FDeletedRows[index][i];
            temp[FDeletedRows[index].Length] = value;
            FDeletedRows[index] = temp;
         }
      }

      protected void AddInsertedRow(string TableName, int value)
      {
      	if( FInsertedRows == null )
         	SetupCurrentRow();
         int index = dataSource.IndexOfTable(TableName);
         if( FInsertedRows[index] == null )
         {
         	FInsertedRows[index] = new int[1];
            FInsertedRows[index][0] = value;
         }
         else
         {
         	int [] temp = new int [FInsertedRows[index].Length + 1];
            for( int i = 0; i < FInsertedRows[index].Length; i++ )
            	temp[i] = FInsertedRows[index][i];
            temp[FInsertedRows[index].Length] = value;
            FInsertedRows[index] = temp;
         }
      }

      protected void RemoveDelRow( int index, int iRemove )
      {
      	if( FDeletedRows == null )
         	SetupCurrentRow();
      	int [] temp = new int [FDeletedRows[index].Length - 1];
         int iSkip = 0;
         for( int i = 0; i < FDeletedRows[index].Length; i++ )
         {
         	if( i != iRemove )
            	temp[i - iSkip] = FDeletedRows[index][i];
            else
               iSkip = 1;
         }
         FDeletedRows[index] = temp;
      }

      protected void RemoveInsRow( int index, int iRemove )
      {
      	if( FInsertedRows == null )
         	SetupCurrentRow();
      	int [] temp = new int [FInsertedRows[index].Length - 1];
         int iSkip = 0;
         for( int i = 0; i < FInsertedRows[index].Length; i++ )
         {
         	if( i != iRemove )
            	temp[i - iSkip] = FInsertedRows[index][i];
            else
               iSkip = 1;
         }
         FInsertedRows[index] = temp;
      }

      protected void RemoveDeletedRow( string TableName, int iRow )
      {
      	if( FDeletedRows == null )
         	return;
         int index = dataSource.IndexOfTable(TableName);
         if( FDeletedRows[index] == null )
           	return;
         for( int i = 0; i < FDeletedRows[index].Length; i++ )
         {
         	if(FDeletedRows[index][i] == iRow)
            	RemoveDelRow(index, i);
         }
      }

      protected void RemoveInsertedRow( string TableName, int iRow )
      {
      	if( FInsertedRows == null )
         	return;
         int index = dataSource.IndexOfTable(TableName);
         if( FInsertedRows[index] == null )
           	return;
         for( int i = 0; i < FInsertedRows[index].Length; i++ )
         {
         	if(FInsertedRows[index][i] == iRow)
            {
            	RemoveInsRow(index, i);
               break;
            }
         }
      }

      public void RemoveDeletedRows( string TableName)
      {
      	if( FDeletedRows == null )
         	return;
         int index = dataSource.IndexOfTable(TableName);
         if( FDeletedRows[index] == null )
           	return;
         FDeletedRows[index] = null;
      }

      public void RemoveInsertedRows( string TableName)
      {
      	if( FInsertedRows == null )
         	return;
         int index = dataSource.IndexOfTable(TableName);
         if( FInsertedRows[index] == null )
           	return;
         FInsertedRows[index] = null;
      }
      // last row here means the row that was current prior to
      // scrolling, and does NOT mean the last row of
      // the table.  The last row of the table is RowCount
      protected int [] LastRow
      {
      	get
         {
         	if( FLastRow == null )
            	SetupCurrentRow();
            return FLastRow;
         }
      }

      private void GetDeletedAndInsertedRowsForTable(string TableName)
      {
      	if( ClassUtils.IsDesignTime(page ) )
            return;
         for( int i = 0; i < page.Session.Count; i++ )
         {
         	string sKey = page.Session.Keys[i];
            if( (sKey.StartsWith( DBWebConst.sDbxDelta ) && sKey.IndexOf( DBWebConst.sDbxDelete ) > 0) ||
            		(sKey.StartsWith( DBWebConst.sDbxDelta ) && sKey.IndexOf( DBWebConst.sDbxInsert ) > 0 ) )
            {
               string tableName;
               int iRow;
               int iCol;
               string oldValue;
               ClassUtils.GetRowColFromKey(sKey, out tableName, out iRow, out iCol, out oldValue);
               if( tableName == TableName )
               {
		            if( sKey.IndexOf( DBWebConst.sDbxDelete ) > 0 )
            			AddDeletedRow(TableName, iRow);
                  else
                  	AddInsertedRow(TableName, iRow);
               }
            }
         }
      }

	  protected void SetupCurrentRow()
	  {
		 DataSet dataSet = dataSourceAccess.DataSetFromDataSource(dataSource.DataSource);
		 if( dataSet != null && dataSet.Tables.Count > 0 )
		 {
			FCurrentRow = new int[dataSet.Tables.Count];
			FRowCount = new int[dataSet.Tables.Count];
			FLastRow = new int[dataSet.Tables.Count];
			FDeletedRows = new int[dataSet.Tables.Count] [];
			FInsertedRows = new int[dataSet.Tables.Count] [];
			for( int i = 0; i < FCurrentRow.Length; i++ )
			 {
			   if( ClassUtils.IsDesignTime(page) || page.Session[dataSet.Tables[i].TableName + DBWebConst.sCurrentRowIndex] == null )
				FCurrentRow[i] = 0;
			   else
				FCurrentRow[i] = Convert.ToInt32(page.Session[dataSet.Tables[i].TableName + DBWebConst.sCurrentRowIndex]);
			   FLastRow[i] = 0;
			   FDeletedRows[i] = null;
			   FInsertedRows[i] = null;
			   GetDeletedAndInsertedRowsForTable(dataSet.Tables[i].TableName);
			   if( !ClassUtils.IsDesignTime(page) && page.Session[dataSet.Tables[i].TableName + DBWebConst.sRowCount] != null )
				  FRowCount[i] = Convert.ToInt32(page.Session[dataSet.Tables[i].TableName + DBWebConst.sRowCount]);
			   else if( (dataSource.DataSource is DataView) &&
				   ((dataSource.DataSource as DataView).Table.TableName == dataSet.Tables[i].TableName) )
				  FRowCount[i] = (dataSource.DataSource as DataView).Count;
			   else if( dataSourceAccess.IsDetailTable( dataSet.Tables[i].TableName ) )
				FRowCount[i] = GetChildRowCount( dataSet.Tables[i].TableName )
								 + GetInsertCount(dataSet.Tables[i].TableName, -1);
               else
      	      	FRowCount[i] = dataSet.Tables[i].Rows.Count
                  				 + GetInsertCount(dataSet.Tables[i].TableName, -1);
         	}
         }
      }

      protected ArrayList RelatedTables
      {
      	get
         {
            return dataSource.RelatedTables;
         }
      }


      protected bool [] HiddenRowsSet
      {
      	get
		 {
			if( FHiddenRowsSet == null && dataSource.RelatedTables != null )
		  {
			FHiddenRowsSet = new bool[dataSource.RelatedTables.Count];
         	   for( int i = 0; i < FHiddenRowsSet.Length; i++ )
            		FHiddenRowsSet[i] = false;
	         }
	         return FHiddenRowsSet;
         }
      }

      protected bool [] DatasetUpdated
      {
      	get
         {
	      	if( FDatasetUpdated == null && dataSource.RelatedTables != null )
   	      {
      	   	FDatasetUpdated = new bool[dataSource.RelatedTables.Count];
         	   for( int i = 0; i < FDatasetUpdated.Length; i++ )
            		FDatasetUpdated[i] = false;
	         }
	         return FDatasetUpdated;
         }
      }

      protected bool [] PostCollectionValuesSet
      {
      	get
         {
	      	if( FPostCollectionValuesSet == null && dataSource.RelatedTables != null )
   	      {
      	   	FPostCollectionValuesSet = new bool[dataSource.RelatedTables.Count];
         	   for( int i = 0; i < FPostCollectionValuesSet.Length; i++ )
            		FPostCollectionValuesSet[i] = false;
	         }
	         return FPostCollectionValuesSet;
         }
      }

      public int GetRowCount(string TableName)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
			if( TableName == RelatedTables[i].ToString() )
            	return RowCount[i];
         return -1;
      }

      public void SetRowCount(string TableName, int value)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
         	if( TableName == RelatedTables[i].ToString() )
            {
               if( value < 0 )
               	value = 0;  
            	RowCount[i] = value;
               if( !ClassUtils.IsDesignTime(page) )
               	page.Session[TableName + DBWebConst.sRowCount] = value;
               break;
            }
      }

    	public int getCurrentRow(string TableName)
      {
         int iCurrentRow = -1;
      	for( int i = 0; i < RelatedTables.Count; i++ )
         	if( RelatedTables[i].ToString() == TableName )
            {
            	iCurrentRow = CurrentRow[i];
               break;
            }
      	return iCurrentRow;
      }

    	public int getFirstParentRow(string TableName)
      {  // if using Session, find the FirstParent for Tablename
         // otherwise, find the current row for TableName's first parent.
         // -- Changed: don't use session;
      	int iParentRow = -1;
         string parentTable = dataSourceAccess.FirstParent(TableName);
         iParentRow = getCurrentRow(parentTable);
         return iParentRow;
      }

      // last row here means the row that was current prior to
      // current scrolling, and does NOT mean the last row of
      // the table.  The last row of the table is RowCount!
    	public int getLastRow(string TableName)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
         	if( RelatedTables[i].ToString() == TableName )
            	return LastRow[i];
      	return -1;
      }

      public void setCurrentRow(string TableName, int value)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
         	if( RelatedTables[i].ToString() == TableName )
            {
	            	CurrentRow[i] = value;
               if( !ClassUtils.IsDesignTime(page) )
               	page.Session[TableName + DBWebConst.sCurrentRowIndex] = value;
               break;
            }
      }

	  public void setLastRow(string TableName, int value)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
         	if( RelatedTables[i].ToString() == TableName )
            {
            	LastRow[i] = value;
               break;
            }
      }

      public void incCurrentRow(string TableName)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
         	if( RelatedTables[i].ToString() == TableName )
            {
            	CurrentRow[i]++;
               break;
            }
      }

      public void decCurrentRow(string TableName)
      {
      	for( int i = 0; i < RelatedTables.Count; i++ )
         {
         	if( RelatedTables[i].ToString() == TableName )
            {
            	CurrentRow[i]--;
               break;
            }
         }
      }
      #endregion row state access functions

	  #region child row management

	  protected int CalcRowCount( string TableName )
	  {
		 Object table = idataSource.GetTableOrView(page, TableName, true);
		 if( table != null )
		 {
			 if( table is DataTable )
			return (table as DataTable).Rows.Count;
			else if (table is DataView)
				return (table as DataView).Count;
		 }
		 return -1;
	  }

	  protected int GetChildRowCount(string TableName)
	  {
			int iRows = CalcRowCount(TableName);
			if( dataSourceAccess.UsesHardDeletes(page) )
				iRows += GetDeleteCount(TableName, -1);
			return iRows;
      }

      // if Parent row is moved, Currentrow all detilas for need to be set back to 0,
      // and detail RowCount needs to be adjusted based on # of rows in view.
      protected void AdjustChildCurrentRow(string TableName, bool bNewRow)
      {
         DataRelation relation = dataSourceAccess.GetRelation(TableName, "");
         while( relation != null )
         {
            DataTable table = relation.ChildTable;
            if( bNewRow )
            {
            	setCurrentRow(table.TableName, -1);
               SetRowCount(table.TableName, 0);
	            page.Session[table.TableName + DBWebConst.sLastRow] = Convert.ToString(-1);
            }
            else
			{
				page.Session[table.TableName + DBWebConst.sLastRow] = getCurrentRow( table.TableName );
         	   DataView view = (idataSource.GetTableOrView(page, table.TableName, true) as DataView);
            	if( view != null )
            		SetRowCount(table.TableName, view.Count);
   	         ResetCurrentRow( 0, table.TableName, 1 );
      	      // child tables are always returned as DataViews
            }
				relation = dataSourceAccess.GetRelation(table.TableName, "");
         }
      }
      #endregion child row management

      #region application statemanagement

      // delete the row.  DataSet behavior with delete is not completely consistent,
      // and we need to
      protected void DeleteRow(int iRow, string TableName, bool bHardRemove, out bool bRemoveKey)
      {
         Object table = idataSource.GetTableOrView(page, TableName, true);
         int iRowCount = -1;
         bRemoveKey = false;
         if( table is DataView )
         	{
            iRowCount = (table as DataView).Count;
            DataView view = table as DataView;
            view[iRow].Delete();
            bRemoveKey = iRowCount > (table as DataView).Count;
            if( !bRemoveKey && bHardRemove )
            {
            	view[iRow].Row.AcceptChanges();
            }
         }
         else if( table is DataTable )
         {
         	iRowCount = (table as DataTable).Rows.Count;
            DataRow dr = (table as DataTable).Rows[iRow];
            dr.Delete();
            bRemoveKey = iRowCount > (table as DataTable).Rows.Count;
            if( !bRemoveKey && bHardRemove )
            {  // if inserted row is being deleted as part of undo, make it a hard-delete
            	dr.AcceptChanges();
            }
         }
         if( !dataSource.AutoRefresh && dataSource.DataSource != null )
         	SaveToSession(table);
      }

      // iRow is the new row, but if its past eof, before bof, or
      // 					on a deleted row, we need to adjust it.
      // iDirection will be +1 or -1, depending if you want to go forward or backward
      // 				in the table
      public void ResetCurrentRow(int iRow, string TableName, int iDirection)
      {
      	int iRowCount = GetRowCount(TableName);
         if( iRowCount - GetDeleteCount(TableName, -1) <= 0 )
         {
         	setCurrentRow(TableName, -1);
            return;
         }
      	if( iRow >= iRowCount )
         	iRow = 0;
         else if( iRow < 0 )
         	iRow = iRowCount -1;
         bool bThroughTable = false;
         while(iRow >= 0 && IsRowDeleted(TableName, iRow) )
         {
         	iRow += iDirection;
            if( (iRow >= iRowCount) || (iRow < 0) )
            {
            	if( bThroughTable )
               	iRow = -1;  // there is no current row: set to -1 and exit loop
               else
               {
                  if( iRow < 0 )
                  	iRow = iRowCount -1;
                  else
                  	iRow = 0;
                  bThroughTable = true;
               }
            }
         }
         setCurrentRow(TableName, iRow);
      }

      private string FindInsertKeyToRemove(string TableName, int iRow)
      {
      	string sInsKey = ClassUtils.GetStartKeyName(DBWebConst.sDbxDelta, TableName, iRow, 0);
      	for( int i = 0; i < page.Session.Count; i++ )
         {
         	string sKey = page.Session.Keys[i];
         	if( sKey.StartsWith(sInsKey) && sKey.IndexOf(DBWebConst.sDbxInsert) > 0 )
            	return sKey;
         }
         return null;
      }

      // if an inserted Row has been removed from the stack of changes due to the
      // fact that is was null and deleted from the dataset, it is necessary
      // to adjust the iRow of all changes made to rows greater than the inserted
      // row which has been deleted.
      protected void AdjustDeltaRows(string TableName, int iRow)
   	{
      	string tableName;
         ArrayList removeList = new ArrayList();
         int row;
         int col;
         string oldValue;
         int iSessionCount = page.Session.Count;
         NameValueCollection nvc = new NameValueCollection();
         string sKey, sValue, sNewKey;
         // to maintain order, store off all delta's into a NameValueCollection,
         // decrementing the row for any rows > iRow (the deleted row)
      	for( int i = 0; i < iSessionCount; i++ )
         {
         	sKey = page.Session.Keys[i];
            if( sKey.StartsWith(DBWebConst.sDbxDelta) )
            {
               sValue = page.Session[i].ToString();
            	ArrayList parentRows = ClassUtils.GetRowColFromKey(sKey, out tableName, out row, out col, out oldValue);
               if( tableName == TableName && row > iRow )
               {
               	removeList.Add(sKey);
               	sNewKey = ClassUtils.GenKeyName(DBWebConst.sDbxDelta, TableName, row-1, col, oldValue, parentRows);
	               nvc.Add(sNewKey, sValue);
                  if( sKey.IndexOf(DBWebConst.sDbxDelete) > 0 )
                  {
                  	RemoveDeletedRow(TableName, iRow);
                     AddDeletedRow(TableName, iRow -1);
                  }
                  else if( sKey.IndexOf(DBWebConst.sDbxInsert) > 0 )
                  {
                  	RemoveInsertedRow(TableName, iRow);
                     AddInsertedRow(TableName, iRow -1);
                  }
               }
               else if( tableName != TableName || row != iRow )
               {  // when master table is DataView, deleting a row causes hard delete
               	removeList.Add(sKey);   // even when that row has data, so ...
	               nvc.Add(sKey, sValue);
               }
               else  // ... remove updates for that row
               	removeList.Add(sKey);
            }
         }
         for( int i = 0; i < removeList.Count; i++ )
         	page.Session.Remove(removeList[i].ToString());
         // restore ordered NameValueCollection;
         for( int i = 0; i < nvc.Count; i++ )
         	page.Session.Add(nvc.Keys[i], nvc[i]);
      }

      protected void UpdateForRemovedKey(Page page, string TableName, int iRow)
      {

      	string sInsKey = FindInsertKeyToRemove(TableName, iRow);
         if( sInsKey != null )
         	{
            RemoveInsert(sInsKey, idataSource.GetTableOrView(page, TableName),
            					TableName, iRow, false);
            AdjustDeltaRows(TableName, iRow);
      	}
         if( iRow > 0 )
      		ResetCurrentRow(iRow -1, TableName, -1);
         else
            ResetCurrentRow(iRow + 1, TableName, 1);
      }


      protected void UpdateApplicationStateForDelete(int iRow, string TableName)
      {
         SetDatasetUpdated(TableName, false);
         if( !dataSourceAccess.IsDetailTable(TableName) && !dataSource.AutoRefresh )
         {
         	bool bRemoveKey;
         	DeleteRow(iRow, TableName, false, out bRemoveKey);
            if( bRemoveKey )
            {  // DataSet behavior is not consistent: if you delete an
            	// inserted row which has data, it remains in the Table, but
               // marked as deleted.  If you remove an inserted row with
               // no data, it is physically removed.  In that case we
               // need to remove all traces of the original insert.
               UpdateForRemovedKey(page, TableName, iRow);
               if( iRow > 0 )
	               ResetCurrentRow(iRow, TableName, -1);
               else
	               ResetCurrentRow(iRow, TableName, 1);
               return;
               // don't add any undo information: this delete cannot be
               // recovered.
            }
         }
      	string sKey = ClassUtils.GenKeyName(DBWebConst.sDbxDelta, TableName, iRow, 0, null, CurrentRow) + DBWebConst.sDbxDelete;
         page.Session[sKey] = true;
         AddDeletedRow(TableName, iRow);
         int iRowCount = GetRowCount(TableName) - GetDeleteCount(TableName, -1);
         if( iRowCount <= 0 )
         {
            setCurrentRow(TableName, -1);
         }
         else
         {
            if( iRow > 0 && !IsRowDeleted(TableName, iRow-1) )
         		ResetCurrentRow(iRow, TableName, -1);
            else
            	ResetCurrentRow(iRow, TableName, 1);
         }
         setLastRow(TableName, -1);
      }

      private void RemoveInsert(string sKey, object TableOrView, string TableName, int iRow, bool bHardRemove)
      {
      	bool bRemoveKey;
         if( !dataSourceAccess.IsDetailTable(TableName) )
         {
	         if( bHardRemove && !dataSource.AutoRefresh )
   	      {
      	   	DeleteRow(iRow, TableName, true, out bRemoveKey);
         	   if( bRemoveKey )
            	   UpdateForRemovedKey(page, TableName, iRow);
	            page.Session.Remove(sKey);
   	         SetRowCount(TableName, GetRowCount(TableName) - 1);
      	      return;
         	}
         }
         RemoveInsertedRow(TableName, iRow);
         page.Session.Remove(sKey);
         SetRowCount(TableName, GetRowCount(TableName) - 1);
      }

      private DataRow RowFromObject(object tableOrView, int iRow)
      {
         if( tableOrView is DataTable )
         {
         	if( iRow < (tableOrView as DataTable).Rows.Count )
	         	return (tableOrView as DataTable).Rows[iRow];
         }
         else if( tableOrView is DataView )
         	if( iRow < (tableOrView as DataView).Count )
            	return (tableOrView as DataView)[iRow].Row;
         return null;
      }

      private void RemoveDelete(	string sKey, object tableOrView, string TableName, int iRow)
      {
         if( !dataSourceAccess.IsDetailTable(TableName) )
         {
	         DataRow row = RowFromObject(tableOrView, iRow);
      	   if( row != null )
         		row.RejectChanges();
         }
         RemoveDeletedRow(TableName, iRow);
         page.Session.Remove(sKey);
      }

      protected void UpdateApplicationStateForInsert(int iRow, string TableName)
      {
      	string sKey = ClassUtils.GenKeyName(DBWebConst.sDbxDelta, TableName, iRow, 0, null, CurrentRow) + DBWebConst.sDbxInsert;
         AddInsertedRow(TableName, iRow);
         page.Session[sKey] = true;
         if( dataSource.AutoRefresh )
         	SetDatasetUpdated(TableName, false);
         SetRowCount(TableName, iRow + 1);
         ResetCurrentRow(iRow, TableName, -1);
      }

      protected DataColumn ColumnFromColumnName(object table, int iColumn)
      {
         if( table is DataTable )
         	return (table as DataTable).Columns[iColumn];
         else if (table is DataView)
         	return (table as DataView).Table.Columns[iColumn];
         return null;
      }

      protected bool IsKeyColumn(DataColumn column)
      {
         DataRelation relation = dataSourceAccess.GetRelation("", column.Table.TableName);
         if( relation != null )
         {
         	for( int i = 0; i < relation.ChildColumns.Length; i++ )
            	if( relation.ChildColumns[i].ColumnName == column.ColumnName )
               	return true;
         }
         relation = dataSourceAccess.GetRelation(column.Table.TableName, "");
         if( relation != null )
         {
         	for( int i = 0; i < relation.ParentColumns.Length; i++ )
            	if( relation.ParentColumns[i].ColumnName == column.ColumnName )
               	return true;
         }
         return false;
      }

      protected void UpdateApplicationStateForUndo(string TableName)
      {
		 if( clientAction == ClientAction.ecaUndoAll && !dataSourceAccess.IsDetailTable(TableName) )
		 {
         	dataSourceAccess.ClearSessionChanges(page);
            return;
         }
      	Object table = idataSource.GetTableOrView(page, TableName, false);
         for(int i = page.Session.Count - 1; i >= 0; i--)
         {
         	if( ( page.Session.Count > i) &&
						( page.Session.Keys[i].StartsWith(DBWebConst.sDbxDelta) ) )
            {
            	int iCol;
               int iRow;
               string sKey = page.Session.Keys[i];
               string tableName = null;
               string oldValue;
               ArrayList parentRows = ClassUtils.GetRowColFromKey(sKey, out tableName, out iRow, out iCol, out oldValue);
               if( tableName == TableName ) // && dataSource.SameParentRow(page, TableName, parentRows) )
               {
		                  if( sKey.IndexOf(DBWebConst.sDbxDelete) > 0 )
                  {
                  	RemoveDelete(sKey, table, TableName, iRow);
                  }
                  else if( sKey.IndexOf(DBWebConst.sDbxInsert) > 0 )
                  {
                  	RemoveInsert(sKey, table, TableName, iRow, true);
                  }
                  else  // undoing column update
                  {
					DataColumn column = ColumnFromColumnName(table, iCol);
					if( !dataSourceAccess.IsDetailTable(TableName) )
					{
						bool bRowDropped = false;
						try
						{
							dataSource.SetColumnValue(table, iRow, column, Convert.ToString(oldValue), out bRowDropped );
						}
						catch(Exception ex)
						{
							HandleException(ex, "", false);
						}
						if( bRowDropped && !dataSource.AutoRefresh)
							UpdateForRemovedKey(page, TableName, iRow);
					}
                  	page.Session.Remove( sKey );
                  }
                  // save session changes
						if( !dataSource.AutoRefresh )
					 {
						if( !HasDelta() )
						{  // no changes left: go back to original dataset
							SaveToSession(dataSource.DataSource);
						}
						else if( !ClassUtils.IsDesignTime(page) )
         					SaveToSession(table);
                  }
               	//* when undoing a column change, set focus back to that row
                  if((clientAction == ClientAction.ecaUndo) && sKey.IndexOf(DBWebConst.sDbxInsert) < 1 )
                  	ResetCurrentRow(iRow, TableName, -1);
                  else if( sKey.IndexOf(DBWebConst.sDbxInsert) > 1 && iRow == getCurrentRow(TableName) )
                  { //* if focus is now on inserted row which you are undoing, move off it
                     if( iRow > 0 )
	                  	ResetCurrentRow(iRow -1, TableName, 1);
                     else
                     	ResetCurrentRow(iRow +1, TableName, 1);
                     setLastRow(TableName, -1);
                  }
                  SetDatasetUpdated(TableName, false);
                  if(clientAction == ClientAction.ecaUndo)
                  	break;
               }
            }
         }
         if(clientAction == ClientAction.ecaUndoAll)
         	ResetCurrentRow(0, TableName, 1);
         else if( getCurrentRow(TableName ) < 0 && GetRowCount(TableName) > 0 )
         	setCurrentRow(TableName, 0 );
      }


      protected void SaveCurrentRows(ArrayList currentRows)
      {
         string tableName;
      	for( int i = 0; i < RelatedTables.Count; i++ )
         {
            tableName = Convert.ToString(RelatedTables[i]);
         	currentRows.Add(getCurrentRow(tableName));
            setCurrentRow(tableName, getLastRow(tableName));
         }
      }

      protected void RestoreCurrentRows(ArrayList currentRows)
      {
      	string tableName;
      	for( int i = 0; i < RelatedTables.Count; i++ )
         {
            tableName = Convert.ToString(RelatedTables[i]);
            setCurrentRow(tableName, Convert.ToInt32(currentRows[i]));
         }
      }
      // first update the Session manager for Insert or Delete
      // 		button hit.
      // then call ForceUpdate to update the Session manager
      // 		for changes made in row values.
		public void UpdateApplicationState(string TableName, int ParentRow)
		{
         // if updating a detail table, we need to create it using the Parent
         // row controlling the detail rows on the last form.  This may not
         // be the current parent row, as it might have already scrolled.
         Object Table = null;
         string ParentTableName = dataSourceAccess.ParentTableNameForChildTable(TableName);
         string firstParent = ParentTableName;
         while( ParentTableName != null )
         {
            firstParent = ParentTableName;
            ParentTableName = dataSourceAccess.ParentTableNameForChildTable(firstParent);
         }
         if( firstParent == null || ParentRow == -1 )
         {
         	Table = idataSource.GetTableOrView(page, TableName, false);
	         ForceUpdate(Table, TableName);
         }
         else
         {
            ArrayList currentRows = new ArrayList( );
            SaveCurrentRows(currentRows);
            try
            {
	            Table = idataSource.GetTableOrView(page, TableName, true);
               if( Table != null )
		         	ForceUpdate(Table, TableName);
            }
            finally
            {
      	      RestoreCurrentRows(currentRows);
            }
         }
		}

	  public bool HasDelta()
	  {
		for( int i = 0; i < RelatedTables.Count; i++ )
			if( HasDelta( RelatedTables[i].ToString() ) )
				return true;
		return false;
	  }
	  
	  public bool HasDelta(string TableName)
	  {
		if( !ClassUtils.IsDesignTime(page) )
		 {
			if( dataSource != null && dataSource.DataSource != null )
			{
				for( int i = 0; i < page.Session.Count; i++ )
			   {
				  string sKey = page.Session.Keys[i];
				if( sKey.StartsWith( DBWebConst.sDbxDelta + TableName) )
						return true;
			   }
			}
		 }
		 return false;
	  }
	  #endregion application state management


	  #region handling client requests

	  private bool HasConstraints( DataTable table )
	  {
		for( int i = 0 ; i < table.Columns.Count; i++ )
         {
         	if( table.Columns[i].AllowDBNull == false )
            	return true;
         }
         return false;
      }

      private bool InsertRow( object table, bool bRemoveInserted, int iAt, ref int LastRow )
      {
      	bool bRetval = false;
         DataRow dr;
         if( table is DataView )
         {  
            DataView dv = (table as DataView);
            int iRowCount = dv.Count;
            DataRowView drv = dv.AddNew();
            if( iRowCount == dv.Count )
               LastRow--;
            bRetval = true;
         }
         else if( table is DataTable )
         {

            DataTable dt = (table as DataTable);
            if( HasConstraints( dt ) )
	            dt.BeginLoadData();
            dr = dt.NewRow();
            if( iAt >= 0 )
            	dt.Rows.InsertAt(dr, iAt);
            else
            	dt.Rows.Add(dr);
            if( bRemoveInserted && dataSource.AutoRefresh )
            	dt.Rows.Remove(dr);
            else if( dataSourceAccess.IsDetailTable(dt.TableName) )
            	dr.AcceptChanges();
            	bRetval = true;
         }
         return bRetval;
      }

      private bool VerifyInsert(string TableName, ref int LastRow)
	  {
		 bool verified = false;
         Object table = idataSource.GetTableOrView(page, TableName, true);
      	try
         {
            verified = InsertRow( table, true, -1, ref LastRow);
            if( !dataSource.AutoRefresh && dataSource.DataSource != null )
            	SaveToSession(table);
         }
         catch(Exception ex)
         {
            HandleException(ex, "", false);
         }
      	return verified;
      }

      // returns true if row position has changed
      protected bool HandleClientAction(ClientAction clientAction, int ARowCount,
      					string TableName, int ParentRow, int RowToSet)
      {
         int currentRow = FCurrentRowHolder;
         int Direction = 1;
         bool FRowChanged = false;
         bool bUndo = false;
      	switch(clientAction)
         {
            case ClientAction.ecaPrevious:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sPrevText;
            	FCurrentRowHolder -= 1;
               Direction = -1;
               FRowChanged = true;
               break;
            case ClientAction.ecaNext:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sNextText;
            	FCurrentRowHolder += 1;
               FRowChanged = true;
               break;
            case ClientAction.ecaFirst:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sFirstText;
               FCurrentRowHolder = 0;
               FRowChanged = true;
               break;
            case ClientAction.ecaLast:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sLastText;
               Direction = -1;
            	FCurrentRowHolder = ARowCount -1;
               FRowChanged = true;
               break;
            case ClientAction.ecaUndo:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sUndoText;
               bUndo = true;
            	break;
            case ClientAction.ecaUndoAll:
            	page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sUndoAllText;
               bUndo = true;
               break;
            case ClientAction.ecaInsert:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sInsertText;
            	UpdateApplicationState( TableName, ParentRow );
               if( VerifyInsert(TableName, ref ARowCount) )
               {
						UpdateApplicationStateForInsert(ARowCount, TableName);
                  FCurrentRowHolder = getCurrentRow(TableName);
      	         FRowChanged = false;
               }
               else
               	clientAction = ClientAction.ecaNone;
               break;
            case ClientAction.ecaDelete:
            case ClientAction.ecaDeleteRow:
               page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sDeleteText;
               if( clientAction == ClientAction.ecaDeleteRow)
               	FRowChanged = true;
               // clicked on Delete Linked Column, need to move to that row.
            	if( clientAction == ClientAction.ecaDeleteRow )
               	FCurrentRowHolder = AddDeletedToRowCount( TableName, RowToSet );
               // else, if clientAction is delete current row, use current row
               UpdateApplicationStateForDelete(FCurrentRowHolder, TableName);
               FCurrentRowHolder = getCurrentRow(TableName);
               break;
            case ClientAction.ecaSetRow:
               FRowChanged = true;
               FCurrentRowHolder = AddDeletedToRowCount( TableName, RowToSet );
               break;
			}
         if( FRowChanged || clientAction == ClientAction.ecaNone)
         	UpdateApplicationState( TableName, ParentRow );
         // only 1 navigator control can change rows at a time.  So if parent
         // table has already set new row, it will reset child row to 0.
         // When this happens, the child row must not be set to the current row
         // value retrieved from page's hidden SRowIndex value for that table.
         if( ( dataSource.IndexOfTable(TableName) <= FRowChangeController ) )
         {
         	if( FErrors.Count > 0 )	// don't move on to next record if error occurred during update
            	ResetCurrentRow(currentRow, TableName, Direction);
            else
         		ResetCurrentRow(FCurrentRowHolder, TableName, Direction);
         }
         if( bUndo )  // might alter CurrentRow position
         {
         	UpdateApplicationStateForUndo(TableName);
         }
         if( dataSourceAccess.IsDetailTable(TableName) &&
         	clientAction != ClientAction.ecaDelete && clientAction != ClientAction.ecaInsert &&
            clientAction != ClientAction.ecaUndo)
         {
         	// if Detail table has deleted rows, this can leave the Current
            // Row value at a state greater than the RowCount
         	SetRowCount(TableName, GetChildRowCount(TableName) );
         }
         return FRowChanged;
      }

      public void SetChangedValues(NameValueCollection postCollection, string TableName)
      {
		 bool bRowChanged = false;
         bool bNewRow = false;
         int Direction = 1;
         int RowToSet = -1;
         int ParentRow = -1;
         int LastRow = -1;
         int FRowCountHolder = Convert.ToInt32(page.Session[TableName + DBWebConst.sRowCount]);
         FCurrentRowHolder = Convert.ToInt32(page.Session[TableName + DBWebConst.sCurrentRowIndex]);
         ParentRow = Convert.ToInt32(page.Session[TableName + DBWebConst.sFirstParentRow]);
         LastRow = Convert.ToInt32(page.Session[TableName + DBWebConst.sLastRow]);
         page.Session.Remove(TableName + DBWebConst.sLastButtonSelected);
         if( LastRow == -1 )
         	setLastRow(TableName, FCurrentRowHolder);
         else
         	setLastRow(TableName, LastRow);
      	postCollectionValues.Clear();
         // These contain values set with RegisterHidden Fields as
         // well as any values from SUBMIT field on page
         for(int i = 0; i < postCollection.Count; i++ )
         {
         	postCollectionValues.Add(postCollection.GetKey(i), postCollection.Get(i));
         }
         clientAction = ClientAction.ecaNone;
			if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sNextText) )
				clientAction = ClientAction.ecaNext;
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sPrevText) )
         {
				clientAction = ClientAction.ecaPrevious;
			Direction = -1;
		 }
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sFirstText) )
				clientAction = ClientAction.ecaFirst;
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sLastText) )
		 {
			Direction = -1;
				clientAction = ClientAction.ecaLast;
		 }
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sApplyText) )
				clientAction = ClientAction.ecaApply;
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sRefreshText) )
				clientAction = ClientAction.ecaRefresh;
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sDeleteText) )
         {  // if the # of undeleted rows is 0, return
         	if( FRowCountHolder - GetDeleteCount(TableName, -1) == 0 )
            	return;
            if( idataSource.HasDetailRecords(page, TableName) )
            	return;
				clientAction = ClientAction.ecaDelete;
         }
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sInsertText) )
         {
				clientAction = ClientAction.ecaInsert;
            bNewRow = true;
         }
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sUndoText) )
				clientAction = ClientAction.ecaUndo;
			else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sUndoAllText) )
				clientAction = ClientAction.ecaUndoAll;
         else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sSetRow ) )
      	{
            RowToSet = Convert.ToInt32(postCollection[TableName + DBWebConst.Splitter + DBWebConst.sSetRow]);
            clientAction = ClientAction.ecaSetRow;
         }
         else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sDeleteRow ) )
      	{
         	if( FRowCountHolder - GetDeleteCount(TableName, -1) == 0 )
            	return;
            if( idataSource.HasDetailRecords(page, TableName) )
            	return;
            clientAction = ClientAction.ecaDeleteRow;
            RowToSet = Convert.ToInt32(postCollection[TableName + DBWebConst.Splitter + DBWebConst.sDeleteRow]);
         }
         else if( ClassUtils.PostCollectionHasValue(postCollection, TableName, DBWebConst.sCancelChange ) )
         	return;
         else  // got here without hitting any known navigator button or grid button
         	UpdateApplicationState( TableName, ParentRow );

		 if( clientAction != ClientAction.ecaApply)
			bRowChanged = HandleClientAction(clientAction, FRowCountHolder, TableName, ParentRow, RowToSet );
		 if( clientAction == ClientAction.ecaApply )
		 {
			page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sApplyText;
			dataSourceAccess.DoOnApplyChanges(page);
		 }
		 else if( clientAction == ClientAction.ecaRefresh )
		 {
			page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sRefreshText;
			dataSourceAccess.DoOnRefresh(page);
		 }
		 if( bRowChanged || clientAction == ClientAction.ecaInsert)
		 {
			page.Session[TableName + DBWebConst.sLastButtonSelected] = DBWebConst.sInsertText;
			 dataSourceAccess.DoOnScroll(TableName, getCurrentRow(TableName), getLastRow(TableName) );
			AdjustChildCurrentRow(TableName, bNewRow);
			for( int i = 0; i < dataSource.RelatedTables.Count; i++ )
				if( dataSource.RelatedTables[i].ToString() == TableName )
				  FRowChangeController = i;
		 }
		 clientAction = ClientAction.ecaNone;
	  }

	  #endregion handling client requests

      #region DataSet updates

      protected bool BooleanCompare(string s1, string s2)
      {
      	if( s1 == "" )
         	return s2 == "";
      	string sTrue = BdwResources.GetString("TrueValues");
         if( sTrue.IndexOf(s1.ToLower()) >= 0 )
         	return sTrue.IndexOf(s2) >= 0;
         else
         	return (s2 != "") && sTrue.IndexOf(s2) < 0;
      }
      
		protected bool CompareObjects(Object o1, Object o2, string dataType)
		{
			bool bCompare = false;
			if( o1 == null )
				bCompare = (o2 == null);
			else if( o2 != null )
         {
         	if( dataType == "System.Boolean" )
            	bCompare = BooleanCompare(o1.ToString(), o2.ToString() );
            else
					bCompare = (o1.ToString() == o2.ToString());
         }
			// else bCompare remains false
			return bCompare;
		}

      protected int[] GetCurrentRows(string TableName)
      {
         int index = dataSource.IndexOfTable(TableName);
      	int [] cr = new int[FCurrentRow.Length];
         for( int i = 0; i < cr.Length; i++ )
         {
         	if(i == index)
            	cr[i] = FCurrentRowHolder;
            else
            	cr[i] = FCurrentRow[i];
         }
         return cr;
      }

      protected bool UnwritableColumn(string dataType)
      {	// { do not localize and consts here }
      	if( dataType.IndexOf("System.Byte[]") > 0  ||
      			(dataType.IndexOf("System.TimeSpan") >= 0)  ||
               (dataType.IndexOf("System.Object") >= 0) )
         	return true;
         return false;
	  }

	  public bool UpdateChanges(Object table, DataTable TableMetaData, NameValueCollection changedValues, string TableName, int iRow)
	  {
		string sColumn;
		 string sCurrent = null;
		 string sNew;
		 int iRowCount;
		 bool bDataSetChanged = false;
		for( int i = 0; i < changedValues.Count; i++ )
		 {
			try
			{
			   sColumn = changedValues.GetKey(i);
			   if( FDuplicateColumns.IndexOf(sColumn) >= 0 )
			   {
				  string sDuplicateColumn = BdwResources.GetString("DuplicateColumn");
				  LogWarning(sColumn + ": " + sDuplicateColumn);
			   }
			   DataColumn thisColumn = TableMetaData.Columns[sColumn];
			   if( thisColumn == null )
					continue;
			   string dataType = thisColumn.DataType.ToString();
			   if( UnwritableColumn(dataType) )
					continue;
			   else if ( !thisColumn.ReadOnly )
			   {
					sCurrent = Convert.ToString(dataSource.GetColumnValue(table, iRow, thisColumn.ColumnName));
					sNew = changedValues[sColumn];
					if( !CompareObjects(sCurrent, sNew, dataType) )
					{
						bDataSetChanged = true;
						try
						{
							if( !dataSourceAccess.IsDetailTable(TableName) )
							{
								bool bRowDropped;
								if( iRow < CalcRowCount(TableName) )
									dataSource.SetColumnValue(table, iRow, thisColumn, sNew, out bRowDropped);
							}
							string sKey = ClassUtils.GenKeyName(DBWebConst.sDbxDelta, TableName, iRow, thisColumn.Ordinal, sCurrent, FCurrentRow);
							page.Session[sKey] = changedValues[sColumn];
						}
						catch( Exception ex )
						{
							HandleException(ex, "", false);
						}
					}
			   }
			}
			catch( Exception ex1 )
			{
				HandleException(ex1, "", false);
			}
		 }
		 return bDataSetChanged;
	  }

	  protected string FindColumnNameForColumnControl(string postKey, NameValueCollection postCollection, string TableName)
	  {
		 if( postKey == (TableName + DBWebConst.sCurrentRowIndex) ||
         		postKey == (TableName + DBWebConst.sRowCount) ||
               postKey.StartsWith(DBWebDataSource.IdentPrefix) )
         	return null;
         string ColumnName = null;
      	for( int i = 0; i < postCollection.Count; i++)
         {
            string sKey = postCollection.GetKey(i);
         	if(sKey == DBWebDataSource.IdentPrefix + postKey)
            {
            	ColumnName = postCollection.Get(i).ToString();
               if( ColumnName.StartsWith(TableName + DBWebConst.Splitter) )
               	return ColumnName.Substring(ColumnName.IndexOf(DBWebConst.Splitter) + 1);
            }
         }
         return null;
      }

      // ReadOnly fields and blob fields are ignored
      protected bool ColumnNotUsed( DataColumn column)
      {
      	string sType = column.DataType.ToString();
         return ( column.ReadOnly ) || UnwritableColumn(column.DataType.ToString() );
      }
      // GetColumnName for persistent Grid Columns.
      //
      protected string FindBoundColumnNameForGrid(string GridId, ref int iGridColumns)
      {	// since each grid is associated with a single table, DataTable and TableName are not needed here
      	object o = page.Session[GridId + Convert.ToString(iGridColumns)];
         if( o != null )
         {
            iGridColumns++;
            return o.ToString();
         }
         return null;
      }

//    assuming that the "_ctl#" portion of the postCollection is the last part of the
//        key value, and that it is sufficient to tell the column #.
//        the ColumnNotUsed() call checks for Blobs and readonly columns;
//        adjust this for blobs, and possibly for user added columns as well.
      protected string FindColumnNameForGrid(DataTable table, string sKey, string TableName, ref int iGridColumns)
      {
         const int MaxColumns = 2000;

         //  Code assumes
         //  1.  GridColumns (ButtonColumn, EditCommandColumn, etc.)
         //  don't show up as submits to postCollection;
         //  2.  _ctrl0 precedes _ctrl1, etc.  However, there is no guarantee
         //  it will start with _ctrl0.  If there are three GridColumns, then
         //  it will start with _ctrl4.  If persistent columns are used,
         //  then FindBoundColumnNameForGrid is called instead.
         // ------------------------------------------------------------------
         //  First, make sure sKey contains grid column
         if( sKey.IndexOf(FAspGridId) > 0 )
         {
         	for( int i = 0; i < MaxColumns; i++ )
            {
         		if( sKey.EndsWith(FAspGridId + Convert.ToString(i) ) )
               {
                  // make sure Column is not blob (not in grid) or read-only field
               	while( iGridColumns < table.Columns.Count &&
                  		ColumnNotUsed( table.Columns[iGridColumns]) )
                  	iGridColumns++;
                  iGridColumns++;
                  if( iGridColumns <= table.Columns.Count )
            			return table.Columns[iGridColumns-1].ColumnName;
               }
            }
         }
         return null;
      }

      private ArrayList GetGridControls(NameValueCollection postCollection, string TableName)
      {
         ArrayList AList = new ArrayList();
      	for( int i = 0; i < postCollection.Count; i++ )
         {
            string sKey = postCollection.GetKey(i);
            string sValue = postCollection.Get(i).ToString();
            // registered in DBWebGrid.PreRender()
				if( sKey.StartsWith(DBWebConst.sDBWebDataGrid + DBWebConst.Splitter + TableName) )
            	AList.Add(sValue + FAspGridId);
         }
         return AList;
      }

      private bool IsGridColumn(string sKey, ArrayList AList, out string GridID)
      {
         string sCheck;
         GridID = null;
      	for( int i = 0; i < AList.Count; i++ )
         {
            sCheck = AList[i].ToString();
         	if( sKey.StartsWith( sCheck ) )
            {
               GridID = sCheck.Substring(0, sCheck.IndexOf(FAspGridId));
            	return true;
            }
         }
         return false;
      }

      protected int GridColumnCount(string sKey)
      {
         if( ClassUtils.IsDesignTime(page) )
         	return 0;
      	int EndName = sKey.IndexOf(FAspGridId);
         if( EndName > 0 )
         {
         	string controlName = sKey.Substring(0, EndName);
            object o = page.Session[controlName + FAspGridId];
            if( o != null )
            	return Convert.ToInt32(o) + 1;
         }
         return 0;
      }

//    Web Column controls call register hidden fields to identify the column
// 			associated with the control
//    Grid controls register their ID's so the Grid Column can be identified.
      protected string FindColumnName(DataTable Table, string postKey, string TableName,
                ArrayList GridList, NameValueCollection postCollection, ref int iColumns)
      {
         string sGridId;
         if( IsGridColumn( postKey, GridList, out sGridId ) )
         {
            if( iColumns == 0 )
            {
					object error = page.Session[sGridId + DBWebConst.sInvalidBoundColumns];
               if( error != null && Convert.ToBoolean(error) )
               	throw new Exception(BdwResources.GetString("InvalidGridColumns") + ":" + sGridId);
            }
            object o = page.Session[sGridId + DBWebConst.sAutoGenerateColumns];
            if( o == null || Convert.ToBoolean(o) )
         		return FindColumnNameForGrid(Table, postKey, TableName, ref iColumns);
            else
            	return FindBoundColumnNameForGrid(sGridId, ref iColumns);
         }
         else
         	return FindColumnNameForColumnControl(postKey, postCollection, TableName);
      }

      protected void SaveToSession(Object Table)
      {
         // Note: if AutoUpdate is false, then DataSource.DataSource is retrieved from
         // session.  Hence, we need to save the version which has been changed,
         // which cannot be DataSource.DataSource.
         // case 1: Table is is DataView, and same DataView as dataSource.DataSource

         if( dataSource.DataSource is DataView && Table is DataView &&
         		(dataSource.DataSource as DataView).Table.TableName == (Table as DataView).Table.TableName )
         {  
         	page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] = Table;
         }
			else if( dataSource.DataSource is DataTable && Table is DataTable &&
         		(dataSource.DataSource as DataTable).TableName == (Table as DataTable).TableName )
               page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] = Table;
         else if( Table is DataTable && dataSource.DataSource is DataSet)
         		page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] = (Table as DataTable).DataSet;
         // detail table
         else if( Table is DataView && dataSource.DataSource is DataSet )
         {
         	page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] = (Table as DataView).Table.DataSet;
         }
		 else if( Table is DataSet )
			page.Session[dataSourceAccess.DataSourceName + DBWebConst.sDataSource] = Table;
//   don't save detail tables which are children of a DataView: the dataset
//   will be updated from session changes information
		}

	  public void ForceUpdate(Object Table, string TableName)
	  {
		string sKey;
		 string sColumnName;
		 DataTable table = null;
		 int iRowCount = 0;
		 if( Table is DataTable )
		 {
			table = Table as DataTable;
			iRowCount = table.Rows.Count;
		 }
		 else if (Table is DataView )
		 {
			table = (Table as DataView).Table;
			iRowCount = (Table as DataView).Count;
		 }
		 if( iRowCount > 0 )
		 {
			int iRow = getLastRow(TableName);
			// if the last row just been deleted don't try to check for changes
			// in it.
			if( iRow == -1 )
				return;
			if( dataSourceAccess.IsDetailTable(TableName) )
				iRow -= GetDeleteCount(TableName, iRow);
			FDuplicateColumns.Clear();
			NameValueCollection changes = new NameValueCollection();
			int iGridColumns = 0;
			ArrayList GridList = GetGridControls(postCollectionValues, TableName);
			try
			{
				for( int i = 0; i < postCollectionValues.Count; i++ )
			{
				 sKey = postCollectionValues.GetKey(i);
				 sColumnName = FindColumnName(table, sKey, TableName, GridList, postCollectionValues, ref iGridColumns);
					if( sColumnName != null && sColumnName != "" )
					{
					if( changes[sColumnName] == null )
						changes.Add( sColumnName, postCollectionValues.Get(i));
					  else
				   {
						FDuplicateColumns.Add(sColumnName);
						iGridColumns --;
					  }
				  }
               }
	         }
            catch(Exception ex)
            {
            	HandleException(ex, "", false);
            }
   	      if( changes.Count > 0 && table != null )
      	   {
               // pass both the DataTable for metadata and the object (DataView/DataTable)
         		if( UpdateChanges(Table, table, changes, TableName, iRow) )
               {
               	if( !dataSource.AutoRefresh && dataSource.DataSource != null )
                  {
                  	SaveToSession(Table);
                  }
			   }
			 }
         }
      }

      #endregion DataSet updates

      #region ErrorHandling
      public void HandleException(Exception exp, string UpdateKey, bool bExtended)
      {
         if( UpdateKey != "" )
         	page.Session.Remove(UpdateKey);
         string sMsg = exp.Message;
         if( !bExtended && FErrors.IndexOf(sMsg) >= 0 )
         	return;
      	FErrors.Add(sMsg);
         if( bExtended )
         {
            DataSet dataSet = dataSourceAccess.DataSetFromDataSource(dataSource.DataSource);
	         for( int i = 0; i < dataSet.Tables.Count; i++ )
   	      {
      	   	DataTable Table = dataSet.Tables[i];
         	   DataRow[] Errors = Table.GetErrors();
            	foreach( DataRow dataRow in Errors )
	            {
   	         	if( dataRow.HasErrors )
      	         {
                     int iIndex = 1;
         	      	System.Data.DataColumn[] ColErrors = dataRow.GetColumnsInError();
            	      foreach( DataColumn dataCol in ColErrors )
                     {
                     	string sColError = BdwResources.GetString("ErrorInColumn");
                        FErrors.Add(sColError + " " + dataCol.ColumnName + ", " + Table.TableName);
                        iIndex++;
                     }
	               }
   	         }
            }
         }
      }

      public void LogWarning(string sMsg)
      {
      	FWarnings.Add(sMsg);
      }

      protected DataTable GetErrorTable(bool bWarnings)
      {
      	DataTable dt = new DataTable();
         string sError;
         if( bWarnings )
         {
	         if( FWarnings.Count > 1 )
   	        sError = BdwResources.GetString("WarningText");
      	   else
         	  sError = BdwResources.GetString("OneWarningText");
         }
         else
         {
	         if( FErrors.Count > 1 )
   	        sError = BdwResources.GetString("ErrorsText");
      	   else
         	  sError = BdwResources.GetString("OneErrorText");
         }
         dt.Columns.Add(sError, Type.GetType("System.String"));

         int iCount;
         if( bWarnings )
         	iCount = FWarnings.Count;
         else
          	iCount = FErrors.Count;
         for( int i = 0; i < iCount; i++ )
         {
         	DataRow dr = dt.NewRow();
            if( bWarnings )
         		dr[0] = FWarnings[i].ToString();
            else
         		dr[0] = FErrors[i].ToString();
         	dt.Rows.Add(dr);
         }
         return dt;
      }

      public string ErrorHtml(string TableName)
      {
      	if( FErrors.Count == 0 )
         	return "";
         StringWriter sw = new StringWriter();
         HtmlTextWriter tw = new HtmlTextWriter(sw);
      	DataGrid errorGrid = new DataGrid();
         errorGrid.ID = "errorGrid";
         errorGrid.DataSource = GetErrorTable(false);
         errorGrid.BorderWidth = dataSource.ErrorDlgBorderWidth;
         errorGrid.BackColor = dataSource.ErrorDlgBackColor;
         errorGrid.ForeColor = dataSource.ErrorDlgForeColor;
         errorGrid.BorderColor = dataSource.ErrorDlgBorderColor;
         errorGrid.DataBind();
         errorGrid.RenderControl(tw);
         return sw.ToString() + "<br>";
      }

      public string WarningsHtml(string TableName)
      {
      	if( FWarnings.Count == 0 )
         	return "";
         StringWriter sw = new StringWriter();
         HtmlTextWriter tw = new HtmlTextWriter(sw);
      	DataGrid errorGrid = new DataGrid();
         errorGrid.ID = "errorGrid";
         errorGrid.DataSource = GetErrorTable(true);
         errorGrid.BorderWidth = dataSource.ErrorDlgBorderWidth;
         errorGrid.BackColor = dataSource.ErrorDlgBackColor;
         errorGrid.ForeColor = dataSource.ErrorDlgForeColor;
         errorGrid.BorderColor = dataSource.ErrorDlgBorderColor;
         errorGrid.DataBind();
         errorGrid.RenderControl(tw);
         return sw.ToString() + "<br>";
      }

      #endregion ErrorHandling

 	}
	#endregion PageStateManager

	#region PageStateManagerCollection
   public class PageStateManagerCollection : ArrayList
   {
		public int Add(PageStateManager o)
		{
      	if( base.IndexOf(o) < 0 )
				return base.Add(o);
         return -1;
		}

		public new PageStateManager this[int index]
		{
			get
			{
				return base[index] as PageStateManager;
			}
			set
			{
				base[index] = value as PageStateManager;
			}
		}
      public PageStateManager FindPageStateManager(Page page)
      {
      	for( int i = 0; i < this.Count; i++ )
         	if(this[i].CheckPage(page))
            	return this[i];
         return null;
      }
   }
   #endregion PageStateManagerCollection

}


