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

using System;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Runtime.Serialization;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.Design;
using System.Text;
using System.Data;

namespace Borland.Data.Web
{
	/// <summary>
	/// DBWebDataSource manages Row, State and Changes for DBWebControls
	/// </summary>
   #region DBWebDataSource

	public enum ClientAction
	{
		ecaNone,
		ecaNext,
		ecaPrevious,
		ecaFirst,
		ecaLast,
		ecaDelete,
		ecaInsert,
		ecaApply,
		ecaRefresh,
		ecaUndo,
		ecaUndoAll,
      ecaSetRow,
      ecaDeleteRow
	}

   public enum ErrorHtmlOption
   {
   	logOnSamePage,
      logOnErrorPage,
      logWithErrorEvent
   }

   public interface IDBDataSource
   {
   	// Get current row position of Table or viwe
   	int GetCurrentRow(Page page, string TableName);
      // Get current row adjusted for Inserts and deletes: used by DataGrid
      int GetDisplayRow(Page page, string TableName);
      // Get total # of rows in Table or view
      int GetRowCount(Page page, string TableName);
      // Get current row adjusted for Inserts and deletes
      int GetDisplayRowCount(Page page, string TableName);
      // Get the row that was current prior to last scroll
      int GetLastRow(Page page, string TableName);
      // Get the value for a column based on current row position of Table
      Object GetColumnValue(Page page, string TableName, string ColumnName);
      // Get the DataSource: DataSet, DataView, or DataTable
      Object GetDataSource(Page page);
      // Get the Table or view referred to by a TableName
      // detail tables are returned only with the rows that match parent key
      Object GetTableOrView(Page page, string TableName);
      // Get the Table or view referred to by a TableName;
      // if bLimitChildRows is false, entired detail table is returned,
      // regardless of parent key
      Object GetTableOrView(Page page, string TableName, bool bLimitChildRows);
      // is there sufficient information to retrieve a DataTable for TableName
      bool IsDataBound(string TableName);
      // have changes been made by this client for Table TableName
      bool HasDelta(Page page, string TableName);
      // get any errors as a DataGrid
      string GetErrorHtml(Page page, string TableName);
												      // get any warnings as a DataGrid
      string GetWarningHtml(Page page, string TableName);
      // control management
      void AddControl(WebControl control);
      void RemoveControl(WebControl control);
      bool HasDetailRecords(Page page, string TableName);
   }

   public interface IDBPostStateManager
   {
   	// get row # of first parent for a Table, -1 if table is itself the first parent.
      int GetFirstParentRow(Page page, string TableName);
      // get row # of parent for a Table, -1 if table is itself the first parent
      int GetParentRow(Page page, string TableName);
      // Get current row unadjusted for Inserts and deletes
   	void SetCurrentRow(Page page, string TableName, int iRow);
      bool SetChangedValues(Page page, NameValueCollection postCollection,
      									string TableName, WebControl control);
      void AddKeyFields(Page page, Object ATableOrView, string TableName,
      								DataRow dr, int iRow);
      bool HasApplyEvent();
   }

   // access to protected properties for DBWebControlPageState
   public interface IDBPageStateManager
   {
	  int IndexOfTable(string tableName);
	  DataSet DataSetFromDataSource(object o);
	  bool IsDetailTable( string TableName );
	  string FirstParent(string TableName);
	  string DataSourceName{get;}
	  bool UsesHardDeletes(Page page);
	  DataRelation GetRelation(string ParentTableName, string ChildTableName);
	  string ParentTableNameForChildTable(string TableName);
	  int GetPhysicalRowCount(Page page, string TableName);
	  void ClearSessionChanges(Page page);
		void DoOnApplyChanges(Page page);
		void DoOnRefresh(Page page);
	  void DoOnError(Page page);
		void DoOnScroll(string TableName, int currentRow, int priorRow);
	  ArrayList GetErrors(Page page);
	  ArrayList GetWarnings(Page page);
   }


   [ToolboxItemFilter("System.Web.UI"),
   ToolboxBitmap(typeof(Borland.Data.Web.DBWebDataSource),
   "Borland.Data.Web.DBWebDataSource.bmp"),
	Designer("Borland.Data.Web.DBWebDataSourceDesigner")]
   public class DBWebDataSource: MarshalByValueComponent, IDBDataSource, IDBPostStateManager, ISupportInitialize, ISerializable, IDBPageStateManager, System.ComponentModel.IListSource
	{
	  public const string IdentPrefix = "DBWid_";
	  protected const string sBorChildDs = "BorlandChildDataSet";

	  private bool FUsesHardDeletes;
	  protected Object FDataSource;
	  protected DBWebControlCollection controls;
	  protected PageStateManagerCollection pageStateManagerCollection;
	  protected ArrayList FRelatedTables;

	  protected Color FErrorDlgBorderColor;
	  protected Color FErrorDlgForeColor;
	  protected Color FErrorDlgBackColor;
	  protected Unit FErrorDlgBorderWidth;
	  protected WebControl FRowStateManager;
	  protected bool FAutoRefresh;
	  // by default, ":_ctrl"
	  private string FAspGridId;

	  private ErrorHtmlOption FErrorOption;


	public DBWebDataSource(): base()
	  {
		 controls = new DBWebControlCollection();
		 FRowStateManager = null;
		FRelatedTables = new ArrayList();
		pageStateManagerCollection = new PageStateManagerCollection();
		 OnApplyChangesRequest = null;
		 OnError = null;
		FErrorDlgBorderColor = System.Drawing.Color.Red;
		FErrorDlgBackColor = System.Drawing.Color.Azure;
		FErrorDlgForeColor = System.Drawing.Color.Black;
		FErrorDlgBorderWidth = Unit.Parse("8px");
		 FAutoRefresh = false;
		 FAspGridId = DBWebConst.sGridColumnID;
		 FErrorOption = ErrorHtmlOption.logOnErrorPage;
		 FUsesHardDeletes = true;
	  }

	  #region ErrorDialog

		[LocalizableCategoryAttribute("ErrorDlg")]
      public System.Drawing.Color ErrorDlgBorderColor
      {
      	get
         {
         	return FErrorDlgBorderColor;
         }
      	set
         {
         	FErrorDlgBorderColor = value;
         }
      }

		[LocalizableCategoryAttribute("ErrorDlg"),
      DefaultValue(ErrorHtmlOption.logOnErrorPage)]
      public ErrorHtmlOption ErrorOption
      {
      	get
         {
         	return FErrorOption;
         }
      	set
         {
         	FErrorOption = value;
         }
      }

		[LocalizableCategoryAttribute("ErrorDlg")]
      public Unit ErrorDlgBorderWidth
      {
      	get
         {
         	return FErrorDlgBorderWidth;
         }
      	set
         {
         	FErrorDlgBorderWidth = value;
         }
      }

		[LocalizableCategoryAttribute("ErrorDlg")]
      public System.Drawing.Color ErrorDlgBackColor
      {
      	get
         {
         	return FErrorDlgBackColor;
         }
      	set
         {
         	FErrorDlgBackColor = value;
         }
      }

		[LocalizableCategoryAttribute("ErrorDlg")]
      public System.Drawing.Color ErrorDlgForeColor
      {
      	get
         {
         	return FErrorDlgForeColor;
         }
      	set
         {
         	FErrorDlgForeColor = value;
         }
      }

      #endregion ErrorDialog

	  #region ISupportsInitialize and IInitializable

      void ISupportInitialize.BeginInit()
      {
         if( FDataSource != null )
         	(FDataSource as ISupportInitialize).BeginInit();
      }
   	void ISupportInitialize.EndInit()
      {
         if( FDataSource != null )
         	(FDataSource as ISupportInitialize).EndInit();
      }
      void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
      {
         if( FDataSource != null )
         	(FDataSource as ISerializable).GetObjectData(info, context);
      }

      IList IListSource.GetList()
      {
      	if( FDataSource  != null )
         	return (FDataSource as IListSource).GetList();
         return null;
      }

      bool IListSource.ContainsListCollection
      {
      	get
         {
      		if( FDataSource  != null )
            	return (FDataSource  as IListSource).ContainsListCollection;
            return false;
         }
      }
      #endregion ISupportsInitialize and IInitializable

   	#region IDBPostStateManager

   	void IDBPostStateManager.SetCurrentRow(Page page, string TableName, int iRow)
      {
      	GetPageStateManager(page).setCurrentRow(TableName, iRow);
      }

      bool IDBPostStateManager.SetChangedValues(Page page, NameValueCollection postCollection,
      									string TableName, WebControl control)
      {
         PageStateManager manager = GetPageStateManager(page);
         // if changed values have already been set for TableName, don't set them
         // again.
      	bool bRetval = !manager.GetPageFieldsWritten(TableName);
      	if( control != null )
       		FRowStateManager = control;
         if( postCollection.Count > 0 && bRetval)
				if( !manager.GetPostCollectionValuesSet(TableName) )
         		manager.SetChangedValues(postCollection, TableName);
         return bRetval;
      }

   	int IDBPostStateManager.GetParentRow(Page page, string TableName)
      {
         DataTable table = ParentTableForChildTable(TableName);
         if( table == null )
         	return -1;
         else
      		return GetPageStateManager(page).getCurrentRow(table.TableName);
      }

   	int IDBPostStateManager.GetFirstParentRow(Page page, string TableName)
      {
      	return GetPageStateManager(page).getCurrentRow(FirstParent(TableName));
      }

      void IDBPostStateManager.AddKeyFields(Page page, Object ATableOrView, string TableName,
      								DataRow dr, int iRow)
      {
      	addKeyFields(page, ATableOrView, TableName, dr, iRow);
      }
      bool IDBPostStateManager.HasApplyEvent()
      {
      	return OnApplyChangesRequest != null;
      }

      #endregion

      #region IDBDataSource

      bool IDBDataSource.HasDelta(Page page, string TableName)
      {
      	return hasDelta(page, TableName);
      }

      private Object GetValue(DataColumn column, DataRow row)
      {
         if( column == null || row.IsNull(column.ColumnName) )
         	return null;
      	else if( column.DataType.ToString() == "System.Char[]" )
         {
         	char[] byteBlobData = new char[0];
            try
            {
            	byteBlobData = (char[])row[column.ColumnName];
               string Text = new string(byteBlobData);
            	return Text;
            }
            catch
            {
            	//TODO: sometimes getting error here
            	return "";
            }
         }
         else
         	return row[column.ColumnName];
      }

      public Object GetColumnValue(Object table, int iRow, string ColumnName)
      {
      	if( iRow >= 0 )
         {
            if( table != null )
            {
            	DataRow row;
               DataColumn column;
               if( table is DataTable )
               {
               	if( iRow >= (table as DataTable).Rows.Count )
                  	return null;
               	column = (table as DataTable).Columns[ColumnName];
                  row = (table as DataTable).Rows[iRow];
                  return GetValue(column, row);
               }
               else if( table is DataView )
               {
                  DataView view = table as DataView;
               	if( iRow >= view.Count )
                  	return null;
                  return GetValue(view.Table.Columns[ColumnName], view[iRow].Row);
               }
            }
         }
         return null;
      }
      Object IDBDataSource.GetColumnValue(Page page, string TableName, string ColumnName)
      {
      	if( !(this as IDBDataSource).IsDataBound(TableName) || ClassUtils.IsEmpty(ColumnName ))
         	return null;
         else
         {
         	int iRow = (this as IDBDataSource).GetCurrentRow(page, TableName);
            if( iRow >= 0 )
            {
	            Object table = getTableOrView(page, TableName, true);
               return GetColumnValue(table, iRow, ColumnName);
            }
         }
         return null;
      }
      int IDBDataSource.GetRowCount(Page page, string TableName)
      {
      	return GetPageStateManager(page).GetRowCount(TableName);
      }
      int IDBDataSource.GetDisplayRowCount(Page page, string TableName)
      {
      	int iCount = GetPageStateManager(page).GetRowCount(TableName);
         if( iCount > 0 && !IsDetailTable(TableName))
         	return iCount - GetPageStateManager(page).GetDeleteCount(TableName, -1);
         else
           return iCount;
      }
   	int IDBDataSource.GetCurrentRow(Page page, string TableName)
      {
      	return GetPageStateManager(page).getCurrentRow(TableName);
      }
   	int IDBDataSource.GetDisplayRow(Page page, string TableName)
      {
         int iRow = GetPageStateManager(page).getCurrentRow(TableName);
         if( iRow > 0 )
         	return iRow - GetPageStateManager(page).GetDeleteCount(TableName, iRow);
         else
         	return iRow;
      }
   	int IDBDataSource.GetLastRow(Page page, string TableName)
      {
      	return GetPageStateManager(page).getLastRow(TableName);
      }
      bool IDBDataSource.IsDataBound(string TableName)
      {
         if( DataSource != null && (DataSource is DataView ||
               DataSource is DataTable ||
         		(TableName != null && (IndexOfTable(TableName) >= 0) ) ) )
      		return true;
         return false;
      }

      Object IDBDataSource.GetTableOrView(Page page, string TableName)
	  {
		return getTableOrView(page, TableName, true);
	  }
      Object IDBDataSource.GetTableOrView(Page page, string TableName, bool bLimitChildRows)
	  {
		return getTableOrView(page, TableName, bLimitChildRows);
      }
      void IDBDataSource.AddControl(WebControl control)
      {
      	if(controls.IndexOf(control) < 0)
         {
      		controls.Add(control);
            if( control is DBWebGrid )
            {
            	DBWebGrid grid = control as DBWebGrid;
               if( !ClassUtils.IsDesignTime(control.Page) && grid.Columns != null )
            		control.Page.Session[control.ID + DBWebConst.sGridColumnCount] = grid.Columns.Count;
            }
         }
      }
      void IDBDataSource.RemoveControl(WebControl control)
      {
      	if(controls.IndexOf(control) >= 0)
         {
      		controls.Remove(control);
         }
      }
      Object IDBDataSource.GetDataSource(Page page)
      {
         if( FAutoRefresh || (page == null) || ClassUtils.IsDesignTime(page) )
      		return FDataSource;
         else if( page.Session[this.DataSourceName + DBWebConst.sDataSource] == null )
         	return FDataSource;
         return page.Session[this.DataSourceName + DBWebConst.sDataSource] as Object;
      }

      string IDBDataSource.GetErrorHtml(Page page, string TableName)
      {
         if( ClassUtils.IsDesignTime(page) || TableName == null || TableName == "" )
         	return null;
         else
      		return GetPageStateManager(page).ErrorHtml(TableName);
      }

      string IDBDataSource.GetWarningHtml(Page page, string TableName)
      {
         if( ClassUtils.IsDesignTime(page) || TableName == null || TableName == "" )
         	return null;
         else
      		return GetPageStateManager(page).WarningsHtml(TableName);
      }

      bool IDBDataSource.HasDetailRecords(Page page, string TableName)
      {
      	DataRelation relation = GetRelation(TableName, "");
         if( relation == null )
         	return false;
         // get child table and see if it has rows.
         object o = getTableOrView(page, relation.ChildTable.TableName, true);
         // has to be a DataView, as detail tables are only returned in this format
      	if( o is DataView )
         	return (o as DataView).Count > 0;
         return false;
      }
      #endregion

	  #region Page State Manager access

      protected PageStateManager GetPageStateManager(Page page)
      {
      	PageStateManager psm = pageStateManagerCollection.FindPageStateManager(page);
         if( psm == null )
         {
         	psm = new PageStateManager(page, this, FAspGridId);
            pageStateManagerCollection.Add(psm);
         }
      	return psm;
      }

      public DataRow[] GetDetailRows(Page page, DataRelation relation)
      {
      	int iRow = GetPageStateManager(page).getCurrentRow(relation.ParentTable.TableName);
      	return relation.ParentTable.Rows[iRow].GetChildRows(relation.RelationName);
      }

	  protected void SetValue(DataColumn column, DataRow row, string value)
      {
         if( column.DataType.ToString() == "System.Char[]" )
         {
         	Char[] charBlobData = new Char[value.Length];
            Byte[] byteBlobData = new Byte[value.Length];
            Encoding.ASCII.GetBytes(value, 0, value.Length, byteBlobData, 0);
            Encoding.ASCII.GetChars(byteBlobData, 0, value.Length, charBlobData, 0);
				row[column.ColumnName] = charBlobData;
			}
         else if( column.DataType.ToString() == "System.Boolean" )
         {
            if( !ClassUtils.IsEmpty(value) && BdwResources.GetString("TrueValues").ToLower().IndexOf(value.ToLower())>= 0 )
         		row[column.ColumnName] = true;
            else
         		row[column.ColumnName] = false;
         }
         else if( !ClassUtils.IsEmpty(value) && column.DataType.ToString() == "System.Char" )
         {
            row[column.ColumnName] = Convert.ToChar(value);
         }
         else if( ClassUtils.IsEmpty(value) )
         {
         	if( column.AllowDBNull )
         		row[column.ColumnName] = DBNull.Value;
            else  // TODO: need to convert string for different field types!
            	row[column.ColumnName] = value;
         }
         else
      		row[column.ColumnName] = value;
      }

      // return the column # that is invalid
      public int InvalidRow(DataRow row, Object table)
      {
      	if( table is DataTable )
         {
         	for( int i = 0; i < (table as DataTable).Columns.Count; i++ )
            {
            	if( (table as DataTable).Columns[i].AllowDBNull == false )
               	if( row[i] == DBNull.Value )
                  	return i;
            }
         }
         return -1;
      }

      public void SetColumnValue(Object table, int iRow, DataColumn column,
      									String value, out bool bRowDropped)
      {
      	bRowDropped = false;
      	if( iRow >= 0 )
         {
         	if( table != null )
            {
            	DataRow row = null;
               if( table is DataTable )
               {
               	int iRows = (table as DataTable).Rows.Count;
                  row = (table as DataTable).Rows[iRow];
                  row.BeginEdit();
                  SetValue(column, row, value);
                  row.EndEdit();
                  if( row.RowState == DataRowState.Added && InvalidRow(row, table) < 0 )
                  	(table as DataTable).EndLoadData();
	               row.AcceptChanges();
                  bRowDropped = (table as DataTable).Rows.Count < iRows;

               }
               else if( table is DataView )
               {
                  DataView view = table as DataView;
                  int iRows = view.Count;
                  row = view[iRow].Row;
                  row.BeginEdit();
                  SetValue(column, row, value);
                  row.EndEdit();
                  // the EndEdit() in following code will delete any inserted records
                  // view[iRow].BeginEdit();
                  // view[iRow][column.ColumnName] = row[column.ColumnName];
                  // view[iRow].EndEdit();
                  // Further, if a row is new, AcceptChanges throws a "row not in table" exception!
                  if( view.Count > iRow )
	                  if( !view[iRow].IsNew )
		               	row.AcceptChanges();
                  bRowDropped = view.Count < iRows;
               }
            }
      	}
      }


      protected bool hasDelta(Page page, string TableName)
      {
         if( ClassUtils.IsDesignTime(page) )
         	return false;
      	return GetPageStateManager(page).HasDelta(TableName);
      }

      protected string FirstParent(string TableName)
      {
         if( FDataSource == null )
         	return null;
         string parentTable = ParentTableNameForChildTable(TableName);
         while( parentTable != null )
         {
         	TableName = parentTable;
         	parentTable = ParentTableNameForChildTable(TableName);
         }
         return TableName;
      }

		[LocalizableCategoryAttribute("DBDataSource"),
		DefaultValue(false)]
      public bool AutoRefresh
      {
      	get
         {
         	return FAutoRefresh;
         }
         set
         {
         	FAutoRefresh = value;
         }
      }
	  #endregion Page State Manager access

	  #region IDBPageStateManager

	  bool IDBPageStateManager.UsesHardDeletes(Page page)
	  {
		if( !ClassUtils.IsDesignTime(page) &&
				page.Session[DBWebConst.sHardDeletes] != null )
			return Convert.ToBoolean(page.Session[DBWebConst.sHardDeletes]);
		return FUsesHardDeletes;
	  }

	  string IDBPageStateManager.DataSourceName
      {
      	get
         {
	      	return DataSourceName;
         }
      }
		void IDBPageStateManager.DoOnApplyChanges(Page page)
      {
      	DoOnApplyChanges(page);
      }
		void IDBPageStateManager.DoOnRefresh(Page page)
      {
      	DoOnRefresh(page);
      }
      void IDBPageStateManager.DoOnError(Page page)
      {
      	DoOnError(page);
      }
		void IDBPageStateManager.DoOnScroll(string TableName, int currentRow, int priorRow)
      {
      	DoOnScroll(TableName, currentRow, priorRow);
      }
      ArrayList IDBPageStateManager.GetErrors(Page page)
      {
         return GetPageStateManager(page).Errors;
      }
      ArrayList IDBPageStateManager.GetWarnings(Page page)
      {
         return GetPageStateManager(page).Warnings;
      }

	  void IDBPageStateManager.ClearSessionChanges(Page page)
	  {
      	ClearSessionChanges(page);
      }


      int IDBPageStateManager.IndexOfTable(string tableName)
      {
      	return IndexOfTable(tableName);
      }
      DataSet IDBPageStateManager.DataSetFromDataSource(object o)
      {
      	return DataSetFromDataSource(o);
      }
      bool IDBPageStateManager.IsDetailTable( string TableName )
      {
      	return IsDetailTable(TableName);
      }
      string IDBPageStateManager.FirstParent(string TableName)
      {
      	return FirstParent(TableName);
      }
      DataRelation IDBPageStateManager.GetRelation(string ParentTableName, string ChildTableName)
      {
      	return GetRelation(ParentTableName, ChildTableName);
      }
      string IDBPageStateManager.ParentTableNameForChildTable(string TableName)
      {
      	return ParentTableNameForChildTable(TableName);
      }

      int IDBPageStateManager.GetPhysicalRowCount(Page page, string TableName)
      {
      	object table = getTableOrView(page, TableName, true);
         if( table is DataView )
         	return (table as DataView).Count;
         else if (table is DataTable)
         	return (table as DataTable).Rows.Count;
         return -1;
      }

	  #endregion

	  #region table access methods

      protected DataSet DataSetFromDataSource(object o)
      {
      	if( o is DataSet )
         	return o as DataSet;
         else if ( o is DataTable )
         	return (o as DataTable).DataSet;
         else if( o is DataView )
         {
         	if( (o as DataView).Table != null )
         		return (o as DataView).Table.DataSet;
            return null;
         }
         else
         	return null;
      }

      // Return name for use in Saving DataSet to Session
      // If more than one DBWebDataSources point to
      // the same DataSet, then only one copy will
      // be stored with session.
      protected string DataSourceName
      {
      	get
         {
				if( FDataSource != null )
            {
            	if( FDataSource is DataSet )
               	return (FDataSource as DataSet).DataSetName;
               else if( FDataSource is DataTable)
               	return (FDataSource as DataTable).TableName;
               else if( FDataSource is DataView )
               	return (FDataSource as DataView).Table.TableName;
            }
            return "";
         }
      }


      [Editor(typeof(Borland.Data.Web.DataSourceEditor), typeof(UITypeEditor)),
		LocalizableCategoryAttribute("DBDataSource"),
		DefaultValue(null)]
		public Object DataSource
		{
			get
			{
            return FDataSource;
			}
			set
			{
				FDataSource = value;
			}
		}


      #region UpdatingDataSet

      protected void UpdateDataColumn(DataRow dataRow,
      										  DataColumn dataColumn,
                                      string ColValue)
		{
			if( dataRow.RowState != DataRowState.Deleted )
			{
				dataRow.BeginEdit();
				if( ColValue == null || ColValue == "" )
					dataRow[dataColumn.ColumnName] = DBNull.Value;
				else
			{
				string dataType = dataColumn.DataType.ToString();
			   if( dataType.StartsWith("System.Char[]") )
                  dataRow[dataColumn.ColumnName] = ColValue.ToCharArray();
			   else
						dataRow[dataColumn.ColumnName] = ColValue;
			}
				dataRow.EndEdit();
			}
		}

		protected void UpdateDataRow(Page page, string sKey,
      								Object value,
      								DataRow dataRow,
                              DataColumn dataColumn)
		{
		 PageStateManager manager = GetPageStateManager(page);
		 if( sKey.IndexOf(DBWebConst.sDbxDelete) > 0 )
         {  // Inserted rows which have been deleted are not inserted
			//  by UpdateTableForInserts() method
				dataRow.Delete();
		 }
		 // inserts are handled prior to UpdateDataRow
		 else if( sKey.IndexOf(DBWebConst.sDbxInsert) < 1 )
		 {
			if( value != null )
				UpdateDataColumn(dataRow, dataColumn, value.ToString() );
            else
            	UpdateDataColumn(dataRow, dataColumn, null );
		 }
		}

      // currently, Asp DataGrid controls have columns with an ID ending in
      // :_ctrl#, where # represents the column number.
      // if future versions change this, allow user to adjust!  
      public void SetGridIdentifier(string identifier)
      {
      	FAspGridId = identifier;
      }

      protected int AdjustRowForDeleted(int iRow, ArrayList HardDeletedRows)
      {
      	int iNewRow = iRow;
      	for( int i = 0; i < HardDeletedRows.Count; i++ )
         {
         	int iDeletedRow = Convert.ToInt32(HardDeletedRows[i]);
            if( iDeletedRow < iRow )
            	iNewRow--;
         }
         return iNewRow;
      }

      protected void UpdateDataRow(Page page,
      					string sKey,
      					Object value,
                     Object TableOrView,
                     string TableName,
					 ArrayList HardDeletedRows,
                     bool bRequiresSameParentRow )
	  {
		if( sKey.StartsWith(DBWebConst.sDbxDelta) )
		 {
			string tableName = null;
            int iRow;
            int iCol;
			string oldValue;
			PageStateManager manager = GetPageStateManager(page);
			ArrayList parentRows = ClassUtils.GetRowColFromKey(sKey, out tableName, out iRow, out iCol, out oldValue);
			// if we're not trying to delete the row, and it's a deleted row,
			//   don't bother going further.
			if( sKey.IndexOf(DBWebConst.sDbxDelete) < 0 )
				if( GetPageStateManager(page).IsRowDeleted(TableName, iRow) )
				return;
			if( tableName == TableName &&
					(!bRequiresSameParentRow || SameParentRow(page, TableName, parentRows ) ) )
            try
			{
			   DataRow dataRow = null;
			   int rowCount = -1;
			   DataColumnCollection dataColumns = null;
			   if( TableOrView is DataTable )
               {
                  rowCount = (TableOrView as DataTable).Rows.Count;
                  if( rowCount > iRow )
					dataRow = (TableOrView as DataTable).Rows[iRow];
                  dataColumns = (TableOrView as DataTable).Columns;
			   }
			   else if( TableOrView is DataView )
               {
                  // inserted row has subsequently been deleted, then
				  // it was not inserted by UpdateTableForInserts() method
				  if( IsDetailTable(TableName) )
					if( manager.IsRowDeleted(TableName, iRow) && manager.IsRowInserted(TableName, iRow) )
						return;
				rowCount = (TableOrView as DataView).Count;
				  iRow = AdjustRowForDeleted(iRow, HardDeletedRows);
				  if( rowCount > iRow )
					dataRow = (TableOrView as DataView)[iRow].Row;
				  dataColumns = (TableOrView as DataView).Table.Columns;
               }
			   // if an inserted row has been deleted, don't update it
			   if( iRow < rowCount && dataRow != null )
			   {
				UpdateDataRow(page, sKey, value, dataRow, dataColumns[iCol]);
				  if( TableOrView is DataView )
				  {
					 int iHardDeleted = rowCount - (TableOrView as DataView).Count;
					 // detail tables seem to have the rows deleted on them
					 if( iHardDeleted > 0 )
					 {
						HardDeletedRows.Add(iRow);
						if( !ClassUtils.IsDesignTime(page) )
							page.Session[DBWebConst.sHardDeletes] = true;
					 }
					 else if( !ClassUtils.IsDesignTime(page) )
					 	page.Session[DBWebConst.sHardDeletes] = false;
				  }
               }
            }
            catch( Exception exp )
            {
            	GetPageStateManager(page).HandleException(exp, sKey, true);
            }
		 }
	  }
      
      // if the same row/col for the same table has been changed twice,
      // only update once.
      protected bool CheckValuesSet(string sKey, ArrayList ColumnsSet)
      {
      	for( int i = 0; i < ColumnsSet.Count; i++ )
         {
         	if( ClassUtils.DoKeysMatch(sKey, ColumnsSet[i].ToString() ) )
				return true;
         }
         return false;
      }

		protected void UpdateDataRows(Page page, Object TableOrView,
      								string TableName, bool bRequiresSameParentRow)
		{
		string sKey;
		ArrayList ColumnsSet = new ArrayList();
		 ArrayList HardDeletedRows = new ArrayList();
		int ApplicationCount = page.Session.Count;
		 for(int i = ApplicationCount - 1; i >= 0; i--)
		 {
			sKey = page.Session.Keys[i];
			// inserts are handled beforehand
			if( sKey.StartsWith(DBWebConst.sDbxDelta) && sKey.IndexOf(DBWebConst.sDbxInsert) < 0 )
			{
				if( !GetPageStateManager(page).GetDatasetUpdated(TableName) || sKey.IndexOf(DBWebConst.sDbxDelete) < 0 )
			   {
					if( !CheckValuesSet(sKey, ColumnsSet ) )
				 {
					ColumnsSet.Add(sKey);
					UpdateDataRow(page, sKey, page.Session[i], TableOrView,
					TableName, HardDeletedRows, bRequiresSameParentRow);
					}
			   }
			}
		 }
		}

      private bool IsParentOfChildTable(string tableName, string childTableName)
      {
      	DataRelation relation = GetRelation("", childTableName);
         while( relation != null )
         {
         	if( relation.ParentTable.TableName == tableName )
            	return true;
			relation = GetRelation("", relation.ParentTable.TableName);
		 }
         return false;
      }

      private void UpdateTableForInserts(Page page, Object table, string TableName)
	  {
		 PageStateManager manager = GetPageStateManager(page);
		 if( manager.GetInsertCount(TableName, -1) > 0 )
		 {
			// need to fill in key field values for child, so temporarily
			// 		cannot be read-only
			SetKeyFieldsReadOnly(TableName, table, true, false);
			 for( int i = 0; i < page.Session.Count; i++ )
   	      {
               string tableName;
               int iRow;
               int iCol;
				string oldValue;
			   string Key = page.Session.Keys[i];
			   bool bProcess = true;
				if( Key.StartsWith(DBWebConst.sDbxDelta + TableName) &&
         					( Key.IndexOf(DBWebConst.sDbxInsert) > 0 ) )
			   {
				  ArrayList KeyRows = ClassUtils.GetRowColFromKey(Key, out tableName, out iRow, out iCol, out oldValue);
						if( manager.IsRowDeleted( TableName, iRow ) )
					continue;
				  if( tableName != TableName )
					continue;
				  if( IsDetailTable(TableName) )
				  {
					  for( int j = 0; j < RelatedTables.Count; j++ )
				   {
						if( RelatedTables[j].ToString() == TableName )
							continue;
						 int iParentRow = manager.getCurrentRow(RelatedTables[j].ToString() );
					  int iThisParentRow = Convert.ToInt32(KeyRows[j]);
						if( iParentRow != iThisParentRow )
						{
							if( IsParentOfChildTable(RelatedTables[j].ToString(), tableName ) )
                           {
	                     		bProcess = false;
		                        break;
                           }
					  }
					 }
                  }
				  // if Table is not the same, or if the parent is on different row, don't insert
				  if( !bProcess )
					continue;
				  int iRowCount = -1;
				  if( table is DataTable )
					iRowCount = (table as DataTable).Rows.Count;
				  else if (table is DataView)
					iRowCount = (table as DataView).Count;
				  if( iRow >= iRowCount )
				  {
					  DataRow dr = null;
				   if( table is DataTable )
      	            {
         	         	dr = (table as DataTable).NewRow();
            	         (table as DataTable).Rows.Add(dr);
				   }
					else if( table is DataView )
					  {
					DataRowView drv = (table as DataView).AddNew();
					   dr = drv.Row;
					 }
					  addKeyFields(page, table, TableName, dr, iRow);
				  }
			   }
            }
			// reset Key fields back to read-only
			SetKeyFieldsReadOnly(TableName, table, true, true);
		 }
	  }

		public void UpdateDataSet(Page page, Object ATableOrView, string TableName,
						bool bRequiresSameParentRow)
		{
		if( ATableOrView == null )
			return;
      	// if dataset is cached then there is no need to update.  Update
		 // is only required after refresh and before ApplyUpdates
		 PageStateManager manager = GetPageStateManager(page);
         // Detail tables are temporary tables that need to be udpated
		 // each time they are retrieved
		 if( manager.GetDatasetUpdated(TableName) && !IsDetailTable(TableName) )
		 {
			return;
         }
			try
			{
         	// first add inserted rows so changes affecting these new rows
			 // can be made.
			 UpdateTableForInserts(page, ATableOrView, TableName);
		   // now update existing rows, including deletes
			UpdateDataRows(page, ATableOrView, TableName, bRequiresSameParentRow);
			manager.SetDatasetUpdated(TableName, true);
		 }
			catch (Exception exp)
			{
			GetPageStateManager(page).clientAction = ClientAction.ecaNone;
				manager.HandleException(exp, "", false);
		 }
		}

    #endregion

      // GetConstraints on a child
      protected void GetConstraintColumnsForChild(string TableName, out DataColumn[] parentConstraintColumns,
      												out DataColumn[] childConstraintColumns)
      {
         parentConstraintColumns = null;
         childConstraintColumns = null;
         DataRelation relation = GetRelation("", TableName);
         if( relation != null )
         {
         	childConstraintColumns = relation.ChildKeyConstraint.Columns;
            parentConstraintColumns = relation.ParentKeyConstraint.Columns;
         }
      }

      protected void GetConstraintColumnsForParent(string TableName, out DataColumn[] parentConstraintColumns,
      												out DataColumn[] childConstraintColumns)
      {
         parentConstraintColumns = null;
         childConstraintColumns = null;
         DataRelation relation = GetRelation(TableName, "");
         if( relation != null )
         {
         	childConstraintColumns = relation.ChildKeyConstraint.Columns;
            parentConstraintColumns = relation.ParentKeyConstraint.Columns;
         }
      }

		// Extract Updated Value for a particular Row/Col/Tablename
	  protected object GetKeyValueFromSession(Page page, string TableName, int iRow, int iCol)
	  {
		string tableName = "";
		 int row = -1;
		 int col = -1;
		 string oldValue;
		 for( int i = 0;i < page.Session.Count; i++ )
		 {
			if( page.Session.Keys[i].StartsWith( DBWebConst.sDbxDelta + TableName )  &&
					page.Session.Keys[i].IndexOf( DBWebConst.sDbxInsert ) < 0 )
			{
				ArrayList parentRows = ClassUtils.GetRowColFromKey(page.Session.Keys[i], out tableName, out row, out col, out oldValue);
				if( tableName == TableName && row == iRow && col == iCol )
				{
					if( !IsDetailTable(TableName) || SameParentRow(page, TableName, parentRows ) )
						return page.Session[i];
					break;
			    }
			}
		 }
		 return null;
	  }


	  protected void addKeyFields(Page page, Object ATableOrView, string TableName,
									DataRow dr, int iRow)

	  {
		 DataColumn[] parentConstraintCols = null;
		 DataColumn[] childConstraintCols = null;
		 DataSet ds = null;

		 if( ATableOrView is DataTable )
			ds = (ATableOrView as DataTable).DataSet;
		 else if( ATableOrView is DataView )
			ds = (ATableOrView as DataView).Table.DataSet;
		 GetConstraintColumnsForChild(TableName, out parentConstraintCols, out childConstraintCols);
		 if( childConstraintCols != null && parentConstraintCols != null)
		 {
			DataRelation relation = GetRelation("", TableName);
			Object ParentTable = getTableOrView(page, relation.ParentTable.TableName, true);
			for( int j = 0; j < childConstraintCols.Length; j++)
			{
			   if(parentConstraintCols[j].ColumnName == childConstraintCols[j].ColumnName)
			   {
				int iParentRow = GetPageStateManager(page).getCurrentRow(relation.ParentTable.TableName);
				  object o = null;
				  if( ParentTable is DataTable )
					o = (ParentTable as DataTable).Rows[iParentRow][parentConstraintCols[j].Ordinal];
                  else if( ParentTable is DataView )
					o = (ParentTable as DataView)[iParentRow].Row[parentConstraintCols[j].Ordinal];
				  dr[childConstraintCols[j].Ordinal] = o;
               }
            }
		 }
		 parentConstraintCols = null;
		 childConstraintCols = null;
         // all of the inserted rows are being inserted prior to inserting any
         // updated data for them.  If there are more than 1 inserted rows that
         // have not been updated, this will cause an error when duplicate blank
         // key fields occur. So key values will be retrieved from the Session
		 // information and added prior to inserting the next row.
		 GetConstraintColumnsForParent(TableName, out parentConstraintCols, out childConstraintCols);
		 if( parentConstraintCols != null )
         {
         	for( int j = 0; j < childConstraintCols.Length; j++)
            {
            	int iCol = parentConstraintCols[j].Ordinal;
				object o = GetKeyValueFromSession(page, TableName, iRow, iCol);
			   DataRow dataRow = null;
               if( ATableOrView is DataTable )
                  dataRow = (ATableOrView as DataTable).Rows[iRow];
               else if (ATableOrView is DataView)
               {
						dataRow = dr;
               }
               if( o != null )
               	dataRow[iCol] = o;
            }
		 }
	  }

      protected DataRelation GetRelation(string ParentTableName, string ChildTableName)
      {  // only called when Relations.Count > 0
         DataSet dataSet = DataSetFromDataSource(FDataSource);
      	for( int i = 0; i < dataSet.Relations.Count; i++ )
         {
         	if( dataSet.Relations[i].ChildColumns != null )
         	{
            	if( dataSet.Relations[i].ParentTable.TableName == ParentTableName ||
            	       dataSet.Relations[i].ChildTable.TableName == ChildTableName)
	               return dataSet.Relations[i];
         	}
         }
         return null;
      }

      public bool SameParentRow(Page page, string tableName, ArrayList parentRows)
      {
         DataTable table = ParentTableForChildTable(tableName);
         // if tablename is parent, don't worry about parent row position
         if( table == null )
         	return true;
      	int iTableIndex = IndexOfTable(table.TableName);
         return (int) parentRows[iTableIndex] == GetPageStateManager(page).getCurrentRow(table.TableName);
      }

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

      public ArrayList RelatedTables
      {
      	get
         {
         	if(FRelatedTables.Count == 0 )
            	GetRelatedTables();
            return FRelatedTables;
         }
      }

      protected void GetRelatedTables()
      {
         ArrayList Temp = new ArrayList();
      	FRelatedTables.Clear();
         DataSet dataSet = DataSetFromDataSource(DataSource);
         if( dataSet != null )
         {
	         for( int i = 0; i < dataSet.Tables.Count; i++ )
            	FRelatedTables.Add(dataSet.Tables[i].TableName);
         }
      }

      public bool IsMasterTable( string TableName )
      {
      	DataRelation relation = GetRelation(TableName, "");
         return relation != null;
      }

      protected bool IsDetailTable( string TableName )
      {
      	DataRelation relation = GetRelation("", TableName);
         return relation != null;
      }

      private DataTable ParentTableForChildTable(string TableName)
      {
      	DataRelation dr = GetRelation("", TableName);
         if( dr == null )
            return null;
         else
         	return dr.ParentTable;
      }

      protected string ParentTableNameForChildTable(string TableName)
      {
      	DataRelation dr = GetRelation("", TableName);
         if( dr == null )
            return null;
         else
			return dr.ParentTable.TableName;
	  }

	  protected Object getTableOrView(Page page, string TableName, bool bLimitChildRows)
	  {
		 Object o = (this as IDBDataSource).GetDataSource(page);
		if(o == null)
			return o;
		if(o is DataView)
         {
            if( (o as DataView).Table == null )
            	return null;
         	if( (o as DataView).Table.TableName == TableName)
            {
               if( !ClassUtils.IsDesignTime(page) )
               {
               	UpdateDataSet(page, o, TableName, true);
               }
         		return o;
            }
		 }
		 DataSet dataSet = DataSetFromDataSource(o);
		 Object table = null;
		 for( int i = 0; i < dataSet.Tables.Count; i++ )
		 {
			if( dataSet.Tables[i].TableName == TableName)
			{
			   if( IsDetailTable(TableName) && bLimitChildRows)
			   {
				table = GetChildTable(page, TableName);
               }
				else
					table = dataSet.Tables[i];

			   if( !ClassUtils.IsDesignTime(page) )
			   {
				UpdateDataSet(page, table, TableName, true);
			   }
			   break;
            }
		 }
		 return table;
	  }

      protected bool HasChild(string TableName)
      {
      	return (GetRelation(TableName, "") != null);
      }

      protected int GetGrandParentRow(Page page, string tableName)
      {
      	if( ClassUtils.IsDesignTime(page) )
         	return 0;
         object o = page.Session[tableName + DBWebConst.sCurrentRowIndex];
         if( o == null )
         	return 0;
         int iRetval = Convert.ToInt32(o);
         if( iRetval == -1 )
         	return 0;
         return iRetval;
      }


      public void SetKeyFieldsReadOnly(string TableName, Object tableOrView,
      					bool SetChild, bool bReadOnlyValue)
      {
         DataColumn[] parentConstraintCols;
         DataColumn[] childConstraintCols;
      	GetConstraintColumnsForChild(TableName, out parentConstraintCols,
      												out childConstraintCols);
         DataTable table = null;
         if( tableOrView is DataTable )
         	table = tableOrView as DataTable;
         else
         	table = (tableOrView as DataView).Table;
         if( parentConstraintCols != null && childConstraintCols != null )
         {
         	for( int j = 0; j < childConstraintCols.Length; j++)
               if(parentConstraintCols[j].ColumnName == childConstraintCols[j].ColumnName)
               {
               	if( SetChild )
	               	table.Columns[childConstraintCols[j].Ordinal].ReadOnly = bReadOnlyValue;
                  else
               		table.Columns[parentConstraintCols[j].Ordinal].ReadOnly = bReadOnlyValue;
               }
         }
      }

      private bool KeyMatch( DataRow row, ArrayList keyValues, DataColumn [] columns )
      {
      	for( int i = 0; i < columns.Length; i++ )
         {
         	string s1 = row[columns[i].ColumnName].ToString();
            string s2 = keyValues[i].ToString();
         	if( s1 != s2 )
            	return false;
         }
         return true;
      }
      private bool SameRow( DataRow targetRow, DataRow sourceRow, DataColumn [] columns )
      {
         for( int i = 0; i < columns.Length; i++ )
         {
         	string columnName = columns[i].ColumnName;
            if( sourceRow[columnName].ToString() != targetRow[columnName].ToString() )
            	return false;
         }
         return columns.Length > 0;
      }

      protected int GetParentRowForChild(Page page, string TableName)
      {
         if( ClassUtils.IsDesignTime(page) )
         	return 0;
		 DataTable table = ParentTableForChildTable(TableName);
		 if( table == null )
			return 0;
		 return GetPageStateManager(page).getCurrentRow(table.TableName);
	  }

	  /* function is only called when child table exists.  It returns a
		 DataView of the child table containing only those rows which are
		 controlled by the parent key*/
	  protected Object GetChildTable(Page page, string TableName)
	  {
			// first, find parent Table for entire Dataset
		 Object ds = (this as IDBDataSource).GetDataSource(page);
		 DataSet dataSet = DataSetFromDataSource(ds);
		 DataRelation parentRelation = null;
		 for( int i = 0; i < dataSet.Tables.Count; i++ )
		{
			parentRelation = GetRelation(dataSet.Tables[i].TableName, "");
			if( (parentRelation != null) &&
				(GetRelation("", dataSet.Tables[i].TableName) == null ) )
			{
				break;
			}
		 }
		 int iRow;
		 DataRelation relation = parentRelation;
		 DataView o = null;
		 PageStateManager manager = GetPageStateManager(page);
		 // create views for each child table until child view is for TableName argument
		 while( relation != null )
		 {
			bool ParentIsTable = false;
			DataView view;
			if( o == null )  // first parent view
			{
			   if( FDataSource is DataView && (FDataSource as DataView).Table == relation.ParentTable)
			   {
					view = (FDataSource as DataView);
			   }
			   else
			   {
					view = new DataView();
					view.Table = relation.ParentTable;
					ParentIsTable = true;
			   }
			}
			else             // a child view
				view = o;
			if( !ClassUtils.IsDesignTime(page) )
			{
				manager.SetDatasetUpdated(view.Table.TableName, false);
				if( ParentIsTable )
					UpdateDataSet(page, view.Table, view.Table.TableName, true);
				else
					UpdateTableForInserts(page, view.Table, view.Table.TableName);
				manager.SetDatasetUpdated(view.Table.TableName, false);
			}
			iRow = GetParentRowForChild(page, relation.ChildTable.TableName);
			if( iRow >= view.Count || iRow < 0 )
			{
				if( !ClassUtils.IsDesignTime(page) && iRow >= 0 )
					UpdateDataSet(page, view, relation.ParentTable.TableName, true);
				if( iRow >= view.Count || iRow < 0 )
					return null;
			}
			o = view[iRow].CreateChildView(relation);
			if( relation.ChildTable.TableName == TableName )
			{
			    SetKeyFieldsReadOnly(TableName, o, true, true );
				break;
			}
            // get new parent relation
            relation = GetRelation(relation.ChildTable.TableName, "");
		 }
		 return o;
	  }
	  #endregion table access methods


	 #region UpdateFullDetailDataSet

     /* after posting, verify row count */
	 protected void ResetRowCount(Page page, DataSet dataSet)
	 {
		PageStateManager manager = GetPageStateManager(page);
		int iRowCount;
		for( int i = 0; i < dataSet.Tables.Count; i++ )
		{
			if( IsDetailTable(dataSet.Tables[i].TableName ) )
			{
				DataView child = (GetChildTable(page, dataSet.Tables[i].TableName) as DataView);
				iRowCount = (child as DataView).Count;
				manager.SetRowCount(dataSet.Tables[i].TableName, iRowCount);
				if( iRowCount < 1 )
					manager.ResetCurrentRow(-1, RelatedTables[i].ToString(), -1);
				else
					manager.ResetCurrentRow(0, RelatedTables[i].ToString(), 0);
				}
			else
			{
				iRowCount = dataSet.Tables[i].Rows.Count;
				manager.SetRowCount(dataSet.Tables[i].TableName, iRowCount);
			}
		}
	 }

	 #region NextKey methods
	  /* retrieve Next Insert Key from changes */
	  private string NextInsertKey(NameValueCollection changes, string tableName)
	  {
		for( int i = 0; i < changes.Count; i++ )
		{
			string Key = changes.GetKey(i);
			if( Key.StartsWith(DBWebConst.sDbxDelta + tableName) &&
					( Key.IndexOf(DBWebConst.sDbxInsert) > 0 ) )
			{
				return Key;
			}
		}
		return null;
	  }

	  /* retrieve Next Delete Key from changes */
	  private string NextDeleteKey(NameValueCollection changes, string tableName)
	  {
		for( int i = 0; i < changes.Count; i++ )
		{
			string Key = changes.GetKey(i);
			if( Key.StartsWith(DBWebConst.sDbxDelta + tableName) &&
					( Key.IndexOf(DBWebConst.sDbxDelete) > 0 ) )
			{
				return Key;
			}
		}
		return null;
	  }

	  /* all inserts have now been handled and removed */
	  private string NextUpdateKey(NameValueCollection changes, string TableName)
	  {
		string Key;
		for( int i = 0; i < changes.Count; i++ )
		{
			Key = changes.Keys[i];
			if( Key.StartsWith(DBWebConst.sDbxDelta + TableName) &&
					Key.IndexOf(DBWebConst.sDbxDelete) < 0 )
				return Key;
		}
		return null;
	  }

	  /* retrieve next key=value update for table */
	  protected string NextUpdateNameAndValue(NameValueCollection changes, DataTable table,
									ArrayList KeyRows, int row, out int iCol, out string Value)
	  {
		int iRow;
		iCol = -1;
		string tableName;
		string Key;
		string oldValue;
		ArrayList parentRows;
		Value = null;
		for( int i = 0; i < changes.Count; i++ )
		{
		   Key = changes.Keys[i];
		   if( Key.IndexOf(DBWebConst.sDbxInsert) < 0 )
		   {
			   parentRows = ClassUtils.GetRowColFromKey(Key, out tableName, out iRow, out iCol, out oldValue);
			   if( row == iRow && tableName == table.TableName )
			   {
				  if( SameParentRows(tableName, KeyRows, parentRows) )
				  {
					 Value = changes[i];
					 return Key;
				  }
			   }
		   }
		}
		return null;
	  }
	 #endregion NextKey methods

	 #region Parent Row verification
	  protected bool SameParentRows(string tableName, ArrayList sourceRows, ArrayList targetRows)
	  {
		string parentTable = ParentTableNameForChildTable(tableName);
		while( parentTable != null )
		{
			int tableIndex = IndexOfTable(parentTable);
			if( Convert.ToInt32(sourceRows[tableIndex]) != Convert.ToInt32(targetRows[tableIndex]) )
				return false;
			parentTable = ParentTableNameForChildTable(parentTable);
		}
		return true;
	  }

	  /* verify that child row changed is the child of the current parent */
	  protected bool IsCorrectChild(PageStateManager manager, string tableName, ArrayList KeyRows)
	  {
		 int iParentRow;
		 int iThisParentRow;
		 /* check each table in dataset and make sure that either:
				1.  its CurrentRow is correct as a parent of this table, or
				2.  its not related to this this table,
				3.  it is this table */
		 for( int j = 0; j < RelatedTables.Count; j++ )
		 {
			if( RelatedTables[j].ToString() == tableName )
				continue;
			iParentRow = manager.getCurrentRow(RelatedTables[j].ToString() );
			iThisParentRow = Convert.ToInt32(KeyRows[j]);
			if( iParentRow != iThisParentRow )
			{
				if( IsParentOfChildTable(RelatedTables[j].ToString(), tableName ) )
				{
					return false;
				}
			}
		 }
		 return true;
	  }

	  /* all parent's CurrentRow need be the correct row for the sought child */
	  protected void SetParentRows(PageStateManager manager, ArrayList KeyRows, DataTable table)
	  {
		string parentTable = ParentTableNameForChildTable(table.TableName);
		while( parentTable != null )
		{
			int tableIndex = IndexOfTable(parentTable);
			manager.setCurrentRow(parentTable, Convert.ToInt32(KeyRows[tableIndex]));
			parentTable = ParentTableNameForChildTable(parentTable);
		}
	  }

	 #endregion Parent Row verification

	 #region Insertion logic
	 protected void addChildKeys(PageStateManager manager, DataTable table,
					ArrayList KeyRows, DataRow dr, int iRow)
	  {
		 DataColumn[] parentConstraintCols = null;
		 DataColumn[] childConstraintCols = null;
		 DataSet ds = null;

		 ds = table.DataSet;
		 GetConstraintColumnsForChild(table.TableName, out parentConstraintCols, out childConstraintCols);
		 if( childConstraintCols != null && parentConstraintCols != null)
		 {
			DataRelation relation = GetRelation("", table.TableName);
			SetParentRows(manager, KeyRows, table);
			Object ParentTable = getTableOrView(manager.GetPage(), relation.ParentTable.TableName, true);
			for( int j = 0; j < childConstraintCols.Length; j++)
			{
			   if(parentConstraintCols[j].ColumnName == childConstraintCols[j].ColumnName)
			   {
				  int iParentRow = manager.getCurrentRow(relation.ParentTable.TableName);
				  object o = null;
				  if( ParentTable is DataTable )
					o = (ParentTable as DataTable).Rows[iParentRow][parentConstraintCols[j].Ordinal];
				  else if( ParentTable is DataView )
					o = (ParentTable as DataView)[iParentRow].Row[parentConstraintCols[j].Ordinal];
				  dr[childConstraintCols[j].Ordinal] = o;
			   }
			}
		 }
	  }

	  protected void AddValuesForInsertedRow(PageStateManager manager,
								  NameValueCollection changes,
								  ArrayList KeyRows, DataTable table,
								  DataRow dr, int row)
	  {
		string Value = null;
		int iCol;
		string Key = NextUpdateNameAndValue(changes, table, KeyRows, row,
							out iCol, out Value);
		while( Key != null )
		{
			SetValue(table.Columns[iCol], dr, Value);
			changes.Remove(Key);
			Key = NextUpdateNameAndValue(changes, table, KeyRows, row,
								out iCol, out Value);
		}
	  }

	  protected void UpdateDetailTableForInserts(PageStateManager manager, NameValueCollection changes, DataTable table)
	  {
		int iRow;
		int iCol;
		string tableName;
		string oldValue;
		string InsertKey = NextInsertKey(changes, table.TableName);
		/* get all changes from session */
		/* will be updating key fields with parent key values, so ....*/
		SetKeyFieldsReadOnly(table.TableName, table, true, false);
		while( InsertKey != null )
		{
			ArrayList KeyRows = ClassUtils.GetRowColFromKey(InsertKey, out tableName, out iRow, out iCol, out oldValue);
			if( !manager.IsRowDeleted( table.TableName, iRow ) && tableName == table.TableName)
			{
				DataRow dr = null;
				dr = table.NewRow();
				table.Rows.Add(dr);
				addChildKeys(manager, table, KeyRows, dr, iRow);
				AddValuesForInsertedRow(manager, changes, KeyRows, table, dr, iRow);
			}
			changes.Remove(InsertKey);
			InsertKey = NextInsertKey(changes, table.TableName);
		}
		SetKeyFieldsReadOnly(table.TableName, table, true, true);
		return;
	  }
	  #endregion Insert logic

	  #region Update logic
	  private void UpdateDetailTableRow(PageStateManager manager,
						DataTable table, string Key, string Value)
	  {
		int iRow, iCol;
		ArrayList parentRows;
		string tableName, oldValue;
		parentRows = ClassUtils.GetRowColFromKey(Key, out tableName, out iRow, out iCol, out oldValue);
		SetParentRows(manager, parentRows, table);
		DataView childTable = GetChildTable(manager.GetPage(), table.TableName) as DataView;
		if( childTable.Count > iRow )
		{
			SetValue(table.Columns[iCol], childTable[iRow].Row, Value);
		}
	  }

	  /* each time a change from changes is put into table it is removed from changes */
	  private void UpdateDetailTableForUpdates(PageStateManager manager,
							NameValueCollection changes, DataTable table)
	  {
		string Key = NextUpdateKey(changes, table.TableName);
		while(Key != null )
		{
			UpdateDetailTableRow(manager, table, Key, changes[Key]);
			changes.Remove(Key);
			Key = NextUpdateKey(changes, table.TableName);
		}
	  }
	  #endregion Update logic

	  #region Delete logic

	  private int AdjustRow(int CurrentRow, ArrayList parentRows, ArrayList rowsDeleted)
	  {
		string tableName, oldValue;
		int ReturnRow = CurrentRow;
		int iRow, iCol;
		ArrayList pRows;
		for( int i = 0; i < rowsDeleted.Count; i++ )
		{
			string Key = rowsDeleted[i].ToString();
			pRows = ClassUtils.GetRowColFromKey(Key, out tableName, out iRow, out iCol, out oldValue);
			/* if a deleted row precedes the current row, adjust */
			if( iRow < CurrentRow )
				if( SameParentRows(tableName, pRows, parentRows) )
                	ReturnRow--;
		}
		return ReturnRow;
	  }
	  private void DeleteDetailTableRow(PageStateManager manager,
						DataTable table, string Key, string Value,
						ArrayList rowsDeleted)
	  {
		int iRow, iCol;
		ArrayList parentRows;
		string tableName, oldValue;
		parentRows = ClassUtils.GetRowColFromKey(Key, out tableName, out iRow, out iCol, out oldValue);
		SetParentRows(manager, parentRows, table);
		DataView childTable = GetChildTable(manager.GetPage(), table.TableName) as DataView;
		iRow = AdjustRow(iRow, parentRows, rowsDeleted);
		if( childTable.Count > iRow )
			childTable[iRow].Row.Delete();
	  }

	  /* each time a change from changes is put into table it is removed from changes */
	  private void UpdateDetailTableForDeletes(PageStateManager manager,
							NameValueCollection changes, DataTable table)
	  {
		ArrayList rowsDeleted = new ArrayList();
		string Key = NextDeleteKey(changes, table.TableName);
		while(Key != null )
		{
			DeleteDetailTableRow(manager, table, Key, changes[Key], rowsDeleted);
			changes.Remove(Key);
			rowsDeleted.Add(Key);
			Key = NextDeleteKey(changes, table.TableName);
		}
	  }
	  #endregion Delete logic

	  private int [] SaveParentRows(PageStateManager manager)
	  {
		int [] currentRows = manager.CurrentRow;
		int [] parentRows = new int[currentRows.Length];
		for( int i = 0; i < parentRows.Length; i++ )
			parentRows[i] = currentRows[i];
		return parentRows;
	  }

	  private void RestoreParentRows(PageStateManager manager, int[] parentRows)
	  {
		for( int i = 0; i < parentRows.Length; i++ )
		{
			manager.CurrentRow[i] = parentRows[i];
			manager.setLastRow(RelatedTables[i].ToString(), -1);
		}
	  }

	  private void UpdateDetailTables(Page page, ArrayList detailTables, NameValueCollection changes)
	  {
		  PageStateManager manager = GetPageStateManager(page);
		  int [] parentRows = SaveParentRows(manager);
		  try
		  {
			  for( int i = 0; i < detailTables.Count; i++)
			  {
				UpdateDetailTableForInserts(manager, changes, detailTables[i] as DataTable);
				UpdateDetailTableForUpdates(manager, changes, detailTables[i] as DataTable);
				UpdateDetailTableForDeletes(manager, changes, detailTables[i] as DataTable);
			  }
		   }
		   finally
		   {
			  RestoreParentRows(manager, parentRows);
		   }
	  }

	  /* load relevant session information for table as NameValueCollection,
		  then pass them on to UpdateDetailTables*/
	  private void UpdateDetailTables(Page page, ArrayList detailTables)
	  {
		NameValueCollection changes = new NameValueCollection();
		string Key;
		string Value;
		for( int i = 0; i < page.Session.Count; i++ )
		{
			Key = page.Session.Keys[i];
			if( Key.StartsWith(DBWebConst.sDbxDelta) )
			{
				Value = Convert.ToString(page.Session[i]);
				changes.Add(Key, Value);
			}
		}
		if( changes.Count > 0 )
			UpdateDetailTables(page, detailTables, changes);
	  }

	  /* Order the detail in such a way that parents precede children;
		 this is necessary so that inserted parent rows preceded inserted
		 detail rows */
	  private void OrderDetailTables(ArrayList detail, DataSet ds)
	  {
		ArrayList tables = new ArrayList();
		for( int i = 0; i < ds.Tables.Count; i++ )
			if( IsDetailTable(ds.Tables[i].TableName ) )
				tables.Add(ds.Tables[i].TableName);
		int index = 0;
		while( tables.Count > 0 )
		{
			// if table's parent is not in the list, add it to detaillist first
			if( tables.IndexOf(ParentTableNameForChildTable(Convert.ToString(tables[index]))) < 0 )
			{
				detail.Add(ds.Tables[tables[index].ToString()]);
				tables.Remove(tables[index].ToString());
			}
			else
			{ // if table's parent is in the list,
			  //  wait till its parent is added to details
				index++;
				if( index >= tables.Count )
					index = 0;
			}
		}
	  }

#endregion UpdateFullDetailDataSet


	  #region DBWebControlCollection events

		public event WebControlEvent OnApplyChangesRequest;
		public event WebControlEvent OnRefreshRequest;
	  public event OnScrollEvent OnScroll;
	  public event OnErrorEvent OnError;

		protected virtual void DoOnScroll(string TableName, int currentRow, int priorRow)
	  {
		 if(OnScroll != null)
		 {
			OnScrollEventArgs e = new OnScrollEventArgs(TableName, currentRow, priorRow);
		  OnScroll(this, e);
		 }
	  }

      protected virtual void DoOnError(Page page)
      {
      	PageStateManager manager = GetPageStateManager(page);
      	if(OnError != null)
         {
            DataSet ds = DataSetFromDataSource((this as IDBDataSource).GetDataSource(page));
         	OnErrorEventArgs e = new OnErrorEventArgs(ds, manager.Errors, manager.Warnings);
            OnError(this, e);
         }
         manager.Errors.Clear();
		 manager.Warnings.Clear();
	  }

	  public void ClearSessionChanges(Page page)
	  {
		ClearSessionState(page, true);
	  }

	  public void ClearSessionState(Page page, bool bResetRowPosition)
	  {

		for( int i = page.Session.Count -1; i >= 0; i-- )
		 {
			if( page.Session.Keys[i].StartsWith(DBWebConst.sDbxDelta) )
					page.Session.Remove(page.Session.Keys[i]);
		 }
		 if( ! FAutoRefresh )
			page.Session[this.DataSourceName + DBWebConst.sDataSource] = FDataSource;
		 int iRowCount;
		 PageStateManager manager = GetPageStateManager(page);
		 for( int i = 0; i < RelatedTables.Count; i++ )
		 {
			manager.RemoveDeletedRows(RelatedTables[i].ToString());
			manager.RemoveInsertedRows(RelatedTables[i].ToString());
			if( bResetRowPosition )
			{
				Object o = getTableOrView(page, RelatedTables[i].ToString(), true);
				if( o is DataTable )
				 iRowCount = (o as DataTable).Rows.Count;
			   else if( o is DataView )
					iRowCount = (o as DataView).Count;
				else
				iRowCount = -1;
				manager.SetRowCount(RelatedTables[i].ToString(), iRowCount);
			   if( iRowCount > 0 )
					manager.ResetCurrentRow(0, RelatedTables[i].ToString(), -1);
				else
				manager.ResetCurrentRow(-1, RelatedTables[i].ToString(), -1);
			}
			page.Session.Remove(RelatedTables[i].ToString() + DBWebConst.sDBWPostCollection);
		 }
	  }

	  public void HardUpdate(Page page)
	  {
		 PageStateManager manager = GetPageStateManager(page);
		 bool bSaveAutoRefresh = FAutoRefresh;
		 bool DetailsNeedUpdate = false;
		 AutoRefresh = true;
		 try
		 {
			bool bUpdated = false;
			DataSet dataSet = DataSetFromDataSource(FDataSource);
			// first, do tables that are not detail tables, so the
			// details have all parent rows prior to attempting a change
			for( int i = 0; i < dataSet.Tables.Count; i++ )
			{
				// if changes have been made to current row make sure they are included
				bUpdated = true;
				int ParentRow = Convert.ToInt32(page.Session[dataSet.Tables[i].TableName + DBWebConst.sFirstParentRow]);
				if( IsDetailTable(dataSet.Tables[i].TableName) )
				{
					Object child = GetChildTable(page, dataSet.Tables[i].TableName);
					manager.ForceUpdate(child, dataSet.Tables[i].TableName);
				}
				else
					manager.UpdateApplicationState(dataSet.Tables[i].TableName, ParentRow);
				if( hasDelta(page, dataSet.Tables[i].TableName ) )
				{
					if( AutoRefresh != bSaveAutoRefresh )  // force update
						GetPageStateManager(page).SetDatasetUpdated(dataSet.Tables[i].TableName, false);

					 DataTable table = dataSet.Tables[i];
					 if( !IsDetailTable(table.TableName ) )
						UpdateDataSet(page, table, table.TableName, false);
					 else
						DetailsNeedUpdate = true;
				  }
			 }
			 if( DetailsNeedUpdate )
			 {
				 // now update detail tables
				 ArrayList DetailTables = new ArrayList();
				 OrderDetailTables(DetailTables, dataSet);
				 UpdateDetailTables(page, DetailTables);
			 }

			 FAutoRefresh = bSaveAutoRefresh;
			 if( bUpdated )
			 {
				if( !ClassUtils.IsDesignTime(page) && !bSaveAutoRefresh )
				{
					page.Session[this.DataSourceName + DBWebConst.sDataSource] = FDataSource;
				}
			 }
		 }
		 catch(Exception ex)
		 {

			manager.HandleException(ex, "", true );
		 }
		 finally
		 {
			FAutoRefresh = bSaveAutoRefresh;
		 }
	  }

		protected virtual void DoOnApplyChanges(Page page)
		{
		DataSet dataSet = DataSetFromDataSource(FDataSource);
		if( OnApplyChangesRequest!= null && dataSet != null )
		{
			HardUpdate(page);
			WebControlEventArgs e = new WebControlEventArgs(dataSet);
			try
			{
				OnApplyChangesRequest(this, e);
				dataSet = DataSetFromDataSource(FDataSource);
				if( dataSet.GetChanges() == null )
				{
					ClearSessionState(page, false);
					ResetRowCount(page, dataSet);
				}
			}
			catch( Exception ex )
			{
			   GetPageStateManager(page).HandleException(ex, "", true);
			}
		 }
	  }

		protected virtual void DoOnRefresh(Page page)
		{
      	if( OnRefreshRequest != null && FDataSource != null )
			{
            DataSet dataSet = DataSetFromDataSource(FDataSource);
				WebControlEventArgs e = new WebControlEventArgs(dataSet);
				OnRefreshRequest(this, e);
			}
         if( !ClassUtils.IsDesignTime(page) && !FAutoRefresh )
         {
         	page.Session[this.DataSourceName + DBWebConst.sDataSource] = FDataSource;
         }
		}

	  #endregion
   }

   #endregion

}
