*--------------------------------------------------------------------------------------------------------------------------------------------------------
* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!!
*--------------------------------------------------------------------------------------------------------------------------------------------------------
*< FOXBIN2PRG: Version="1.21" SourceFile="_ca_base.vcx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries)
*
*
DEFINE CLASS _cabase AS _cursoradapter OF "_baza.vcx" 
 	*< CLASSDATA: Baseclass="cursoradapter" Timestamp="" Scale="Pixels" Uniqueid="" />

	*<DefinedPropArrayMethod>
		*m: cbaseselectcmd_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		*m: cselectcmdfilter_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		*m: cselectorderby_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		*m: cselectotherclauses_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		*m: _aftercursorfillrefresh		&& Fired by the AfterCursorFill and AfterCursorRefresh events using BINDEVENT. This method is useful for placing code to create indexes on the cursor after the cursor is created by the CursorAdapter.
		*m: _buildselectcmd		&& Called by _ca_dmlcode and _ca_autoupdate methods to create the SQL for the SelectCmd property.
		*m: _ca_adosetup		&& Called by the Init to setup ADO objects
		*m: _ca_readme		&& Information on how to use this Class Library
		*p: cbaseselectcmd		&& The base SQL command to use for creating the SQL for the SelectCmd property.
		*p: cselectcmdfilter		&& The filter to use in the WHERE clause of the SelectCmd.
		*p: cselectorderby		&& Optional ORDER BY clause to be added to the SelectCmd. Do NOT include the words ORDER BY in your list.
		*p: cselectotherclauses		&& Optional clause(s) to be added to the SelectCmd AFTER the ORDER BY clause (e.g., GROUP BY and HAVING). You will need to include those key phrases in your string.
		*p: lautogenerate		&& Set to TRUE if the CA is to perform auto updates where VFP generates the Insert, Update and Delete SQL. See options under the lCallStoredProcs property if this is set to FALSE.
		*p: lcallstoredprocs		&& Set to TRUE if you want the values of the *Cmd properties to be set by the _CA_SPCode method of each CA subclass. Otherwise, the *Cmd properties will be set by the _CA_DMLCode method.
		*p: lnodataonload		&& Set to TRUE if you want the cursor to be created with NO DATA returned from the source tables(s).
		*p: lschematruncated		&& Set to TRUE by the CA VCX Builder if the CursorSchema value displayed in the Property Sheet has been been shortened to comply with the 255 character display limit. This value is replaced by the complete schema at runtime when the _CA_INIT method fires.
		*p: lusecursorschema		&& Set to TRUE if you want the CursorFill method to use the structure in the CursorSchema property.
		*p: nfilloptions		&& See VFP Help on CursorFill for discussion on the use of nOptions. The default is -1 but set to 0 in the CursorFill method if the DataSourceType is not ADO.
		*p: oado_command		&& ADO Command Object
		*p: oado_conn		&& ADO Connection Object.
		*p: oado_rs		&& ADO Record Source Object
	*</DefinedPropArrayMethod>

	*<PropValue>
		Alias = "cursor1"
		BufferModeOverride = 5
		cbaseselectcmd = 
		Comment = "Cursor Adapter Baseclass"
		cselectcmdfilter = 
		cselectorderby = 
		cselectotherclauses = 
		FetchSize = -1
		Flags = 0
		Height = 22
		lautogenerate = .T.
		lcallstoredprocs = .F.
		lnodataonload = .F.
		lschematruncated = .F.
		lusecursorschema = .F.
		Name = "_cabase"
		nfilloptions = -1
		oado_command = .NULL.
		oado_conn = .NULL.
		oado_rs = .NULL.
		SendUpdates = .F.
		UseDeDataSource = .T.
		WhereType = 1
		Width = 23
	*</PropValue>
	
	PROCEDURE AfterCursorFill
		LPARAMETERS lUseCursorSchema, lNoDataOnLoad, cSelectCmd, lResult
		DODEFAULT(lUseCursorSchema, lNoDataOnLoad, cSelectCmd, lResult)
		THIS._AfterCursorFillRefresh()
		NODEFAULT
	ENDPROC

	PROCEDURE AfterCursorRefresh
		LPARAMETERS cSelectCmd, lResult
		DODEFAULT(cSelectCmd, lResult)
		THIS._AfterCursorFillRefresh()
		NODEFAULT
	ENDPROC

	PROCEDURE AutoOpen
		*** Setup code: DO NOT REMOVE
		if not pemstatus(This, '__VFPSetup', 5)
			This.AddProperty('__VFPSetup', 1)
			This.Init()
		endif
		*** End of Setup code: DO NOT REMOVE
		
	ENDPROC

	PROCEDURE BeforeDelete
		LPARAMETERS cFldState, lForce, cDeleteCmd
		IF UPPER(THIS.DeleteCmdDataSourceType) = [NATIVE]
			cDeleteCmd = CHRTRAN(cDeleteCmd, [?], [])
		ELSE
			IF EMPTY(THIS.DeleteCmdDataSourceType) AND UPPER(THIS.DataSourceType) = [NATIVE]
				cDeleteCmd = CHRTRAN(cDeleteCmd, [?], [])
			ENDIF
		ENDIF
		LOCAL llRetVal
		llRetval = DODEFAULT(cFldState, lForce, cDeleteCmd)
		NODEFAULT
		RETURN llRetVal
		
	ENDPROC

	PROCEDURE BeforeInsert
		LPARAMETERS cFldState, lForce, cInsertCmd
		IF UPPER(THIS.InsertCmdDataSourceType) = [NATIVE]
			cInsertCmd = CHRTRAN(cInsertCmd, [?], [])
		ELSE
			IF EMPTY(THIS.InsertCmdDataSourceType) AND UPPER(THIS.DataSourceType) = [NATIVE]
				cInsertCmd = CHRTRAN(cInsertCmd, [?], [])
			ENDIF
		ENDIF
		LOCAL llRetVal
		llRetVal = DODEFAULT(cFldState, lForce, cInsertCmd)
		NODEFAULT
		RETURN llRetVal
		
	ENDPROC

	PROCEDURE BeforeUpdate
		LPARAMETERS cFldState, lForce, nUpdateType, cUpdateInsertCmd, cDeleteCmd
		*!*	If the nUpdateType is 2 [update by deleting record first then
		*!*	insert new data -- bad idea] the SQL in the DeleteCmd is used.
		IF UPPER(THIS.UpdateCmdDataSourceType) = [NATIVE]
			cUpdateInsertCmd = CHRTRAN(cUpdateInsertCmd, [?], [])
			cDeleteCmd = CHRTRAN(cDeleteCmd, [?], [])
		ELSE
			IF EMPTY(THIS.UpdateCmdDataSourceType) AND UPPER(THIS.DataSourceType) = [NATIVE]
				cUpdateInsertCmd = CHRTRAN(cUpdateInsertCmd, [?], [])
				cDeleteCmd = CHRTRAN(cDeleteCmd, [?], [])
			ENDIF
		ENDIF
		LOCAL llRetVal
		llRetVal = DODEFAULT(cFldState, lForce, nUpdateType, cUpdateInsertCmd, cDeleteCmd)
		NODEFAULT
		RETURN llRetVal
		
	ENDPROC

	PROTECTED PROCEDURE cbaseselectcmd_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		LPARAMETERS tcNew
		IF VARTYPE(tcNew) <> [C]
			RETURN .f.
		ENDIF
		tcNew = ALLTRIM(tcNew)
		THIS.cBaseSelectCmd = tcNew
		THIS._BuildSelectCmd()
	ENDPROC

	PROTECTED PROCEDURE cselectcmdfilter_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		LPARAMETERS tcNew
		IF VARTYPE(tcNew) <> [C]
			RETURN .f.
		ENDIF
		tcNew = ALLTRIM(tcNew)
		IF LEFT(UPPER(tcNew), 6) = [WHERE ]
			IF LEN(tcNew) = 6
				tcNew = []
			ELSE
				tcNew = ALLTRIM(SUBSTR(tcNew, 7))
			ENDIF
		ENDIF
		THIS.cSelectCmdFilter = tcNew
		THIS._BuildSelectCmd()
	ENDPROC

	PROTECTED PROCEDURE cselectorderby_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		LPARAMETERS tcNew
		IF VARTYPE(tcNew) <> [C]
			RETURN .f.
		ENDIF
		tcNew = ALLTRIM(tcNew)
		IF LEFT(UPPER(tcNew), 9) = [ORDER BY ]
			IF LEN(tcNew) = 9
				tcNew = []
			ELSE
				tcNew = ALLTRIM(SUBSTR(tcNew, 10))
			ENDIF
		ENDIF
		THIS.cSelectOrderBy = tcNew
		THIS._BuildSelectCmd()
	ENDPROC

	PROTECTED PROCEDURE cselectotherclauses_assign		&& Assign method that fires the _BuildSelectCmd if the Property value is changed.
		LPARAMETERS tcNew
		IF VARTYPE(tcNew) <> [C]
			RETURN .f.
		ENDIF
		tcNew = ALLTRIM(tcNew)
		THIS.cSelectOtherClauses = tcNew
		THIS._BuildSelectCmd()
	ENDPROC

	PROCEDURE CursorFill
		LPARAMETERS lUseCursorSchema, lNoData, nOptions, uSource
		IF NOT PEMSTATUS(THIS, 'lADO_Done', 5)
			THIS.AddProperty('lADO_Done')
		ENDIF
		IF THIS.DataSourceType = [ADO] AND NOT THIS.lADO_Done
			RETURN
		ENDIF
		LOCAL llSchema, llNoData, lnOptions, luSource
		llSchema  = lUseCursorSchema
		llNoData  = lNoData
		lnOptions = nOptions
		luSource  = uSource
		IF VARTYPE(THIS.lUseCursorSchema) <> "L"
			THIS.lUseCursorSchema = .F.
		ENDIF
		IF VARTYPE(THIS.lNoDataOnLoad) <> "L"
			THIS.lNoDataOnLoad = .F.
		ENDIF
		IF VARTYPE(THIS.nFillOptions) <> "N"
			THIS.nFillOptions = -1
		ENDIF
		IF PCOUNT() < 1 OR VARTYPE(llSchema) <> "L"
			llSchema = THIS.lUseCursorSchema
		ENDIF
		IF PCOUNT() < 2 OR VARTYPE(llNoData) <> "L"
			llNoData = THIS.lNoDataOnLoad
		ENDIF
		IF PCOUNT() < 3 OR VARTYPE(lnOptions) <> "N"
			lnOptions = IIF(THIS.DataSourceType = [ADO], THIS.nFillOptions, 0)
		ENDIF
		LOCAL llRetVal
		IF THIS.DataSourceType = [ADO]
			llRetVal = DODEFAULT(llSchema, llNoData, lnOptions, THIS.oADO_Command)
		ELSE
			IF PCOUNT() = 4
				llRetVal = DODEFAULT(llSchema, llNoData, lnOptions, luSource)
			ELSE
				llRetVal = DODEFAULT(llSchema, llNoData, lnOptions)
			ENDIF
		ENDIF
		NODEFAULT
		RETURN llRetval
		
	ENDPROC

	PROCEDURE Destroy
		WITH THIS
			.oADO_Conn = .null.
			.oADO_RS = .null.
			.oADO_Command = .null.
			.DataSource = .null.
			.UpdateCmdDataSource = .null.
			.DeleteCmdDataSource = .null.
			.InsertCmdDataSource = .null.
		ENDWITH
		
	ENDPROC

	PROCEDURE Init
		local llReturn
		do case
			case not pemstatus(This, '__VFPSetup', 5)
				This.AddProperty('__VFPSetup', 0)
			case This.__VFPSetup = 2
				This.__VFPSetup = 0
				return
		endcase
		llReturn = dodefault()
		*!*	BINDEVENT(THIS, 'AfterCursorFill', THIS, '_AfterCursorFillRefresh', 1)
		*!*	BINDEVENT(THIS, 'AfterCursorRefresh', THIS, '_AfterCursorFillRefresh', 1)
		*!*	For some reason the BINDEVENT to these methods is generating a VFP
		*!*	error "No Parameter Statement found". So for now, I am commenting the
		*!*	BINDEVENTs and placing code directly in these methods that call the
		*!*	_AfterCursorFillRefresh
		IF THIS.DataSourceType = [ADO]
			THIS._CA_ADOSetup()
		ENDIF
		*** Select connection code: DO NOT REMOVE
		set multilocks on
		*** End of Select connection code: DO NOT REMOVE
		*** Setup code: DO NOT REMOVE
		
		*** End of Setup code: DO NOT REMOVE
		if This.__VFPSetup = 1
			This.__VFPSetup = 2
		endif
		return llReturn
		
	ENDPROC

	PROCEDURE _aftercursorfillrefresh		&& Fired by the AfterCursorFill and AfterCursorRefresh events using BINDEVENT. This method is useful for placing code to create indexes on the cursor after the cursor is created by the CursorAdapter.
		*!*	This code is fired AFTER any code in the AfterCursorFill
		*!*	or the AfterCursorRefresh methods is executed. To run
		*!*	code in this method BEFORE executing code in those methods,
		*!*	remove the 5th parameter in the BINDEVENT commands in the
		*!*	INIT method of this class.
	ENDPROC

	PROCEDURE _buildselectcmd		&& Called by _ca_dmlcode and _ca_autoupdate methods to create the SQL for the SelectCmd property.
		THIS.SelectCmd = THIS.cBaseSelectCmd ;
			+ IIF(EMPTY(THIS.cSelectCmdFilter), [], [ WHERE ] + THIS.cSelectCmdFilter) ;
			+ IIF(EMPTY(THIS.cSelectOrderBy), [], [ ORDER BY ] + THIS.cSelectOrderBy) ;
			+ IIF(EMPTY(THIS.cSelectOtherClauses), [], [ ] + ALLTRIM(THIS.cSelectOtherClauses))
	ENDPROC

	PROCEDURE _ca_adosetup		&& Called by the Init to setup ADO objects
		IF NOT PEMSTATUS(THIS, 'lADO_Done', 5)
			THIS.AddProperty('lADO_Done')
		ENDIF
		IF THIS.DataSourceType <> [ADO] OR THIS.lADO_Done
			RETURN
		ENDIF
		*!* You must have already established an ADO Connection Object.
		*!*	The assumption is that you establish this connection when
		*!*	the application is started and the object reference is being
		*!*	stored in either a public/private variable [this is OK] or an
		*!*	Application Object [goApp, better way]. Then set the value of
		*!*	the oADO_Conn property in this CA baseclass to the goApp
		*!*	property or variable.
		LOCAL luOldValue
		luOldValue = THIS.oADO_Conn
		IF TYPE("THIS.oADO_Conn") = "C" AND NOT EMPTY(THIS.oADO_Conn)
			THIS.oADO_Conn = EVALUATE(THIS.oADO_Conn)
		ENDIF
		IF TYPE("THIS.oADO_Conn") <> "O" AND TYPE("THIS.DataSourceType") = "O"
			THIS.oADO_Conn = THIS.DataSourceType
		ENDIF
		IF TYPE("THIS.oADO_Conn.Provider") <> "C"	
			*!* Old Code that was failing: IF NOT PEMSTATUS(THIS.oADO_Conn, [Provider], 5)
			*!*	if the Provider property does not exist,
			*!*	the object is not an OLEDB provider.
			THIS.oADO_Conn = luOldValue
			RETURN .F.
		ENDIF
		THIS.oADO_RS      = NEWOBJECT('ADODB.Recordset')
		THIS.oADO_Command = NEWOBJECT('ADODB.Command')
		IF TYPE("THIS.oADO_RS") <> "O" OR TYPE("THIS.oADO_Command") <> "O"
			RETURN .f.
		ENDIF
		THIS.oADO_Command.ActiveConnection = THIS.oADO_Conn
		WITH THIS.oADO_RS
			.ActiveConnection = THIS.oADO_Conn
			.CursorType       = 1  && adOpenKeyset
			.CursorLocation   = 3  && adUseClient
			.LockType         = 3  && adLockOptimistic
		ENDWITH
		THIS.DataSource = THIS.oADO_RS
		IF NOT THIS.lAutoGenerate
			THIS.UpdateCmdDataSourceType = [ADO]
			THIS.UpdateCmdDataSource     = THIS.oADO_Command
			THIS.DeleteCmdDataSourceType = [ADO]
			THIS.DeleteCmdDataSource     = THIS.oADO_Command
			THIS.InsertCmdDataSourceType = [ADO]
			THIS.InsertCmdDataSource     = THIS.oADO_Command
		ENDIF
		THIS.lADO_Done = .t.
		
	ENDPROC

	PROCEDURE _ca_readme		&& Information on how to use this Class Library
		*!*	CursorAdapter Class Generator for Visual FoxPro 8
		*!*	
		*!*	Author:	Mark E. McCasland
		*!*			M & J Software
		*!*			Hurst TX USA
		*!*			http://www.mctweedle.com
		
		* * * * * *   I N T R O D U C T I O N   * * * * * *
		
		*!*	Please read these comments completely. There are GOTCHAS and
		*!*	WARNINGS below as well as directions for how to use the Class
		*!*	Libraries created by this builder.
		*!*	
		*!*	If you have any questions, suggestions, bug reports, please
		*!*	post a new message on the UT -- http://www.UniversalThread.com
		*!*	or the microsoft.public.fox.vfp.dbc News Group on the Microsoft
		*!*	VFP Public News Server.
		
		* * * * * *   A B O U T   T H I S   B U I L D E R   * * * * * *
		
		*!*	This class is designed to connect to a DBC, Oracle or SQL Server,
		*!*	retrieve all tables in that database, and create a CursorAdapter
		*!*	subclass for each table. This class generates the code that is
		*!*	needed at runtime to populate the properties in each CA in order
		*!*	for VFP to auto-generate the Insert, Update, Delete SQL or to
		*!*	allow you to programmatically handle these functions. This code
		*!*	is then written into the AUTOOPEN, INIT and other methods of
		*!*	each CA subclass.
		*!*	
		*!*	The creation of a PRG-based Class Library is not available via
		*!*	this builder. You can use the View Class Code option in the Class
		*!*	Browser to get the complete code to create a PRG class.
		*!*	
		*!*	If you are using this builder against SQL Server or Oracle, ODBC
		*!*	will be used to connect to the database even if the VCX created
		*!*	by this builder is to use ADO.
		*!*	
		*!*	The first step done by the builder is the creation of a Class
		*!*	Library [VCX] with a CursorAdapter baseclass. Then, all the
		*!*	Table-specific CursorAdapters are created as sub-classes of the
		*!*	CA baseclass. This allows you to set "common" properties at the
		*!*	baseclass level like the DataSource [connection handle] and
		*!*	DataSourceType properties.
		*!*	
		*!*	Please keep in mind this is a design time UTILITY and is not
		*!*	intended to be "pretty" with respect to the UI. However, it does
		*!*	its intended job quite nicely.
		
		* * * * * *   W H A T ' S   N E W   * * * * * * * * * * * * * * * * *
		
		*!*	Version 2.07.00 [February 2004]
		*!*	
		*!*		Fixed a bug in Cr8_DMLCode where, if all fields are a combination
		*!*		of PK fields and/or excluded fields, an orphaned ENDTEXT would
		*!*		would be created which created a compile error.
		*!*	
		*!*		Enhanced the "error" reporting for "BadTables".
		*!*	
		*!*		Fixed problem with creating a separate VCX for each table.
		*!*	
		*!*	Version 2.06.00 [October 2003]
		*!*	
		*!*		Changed how the DeleteCmd property in each CA sub-class is
		*!*		populated by the Cr8_DMLCode method of this builder. It is no
		*!*		longer directly populated in each CA sub-class by the builder.
		*!*		It is now populated via code in the _ca_dmlcode method at runtime.
		*!*		This corrects a problem where writing that value directly to the
		*!*		property creats an error during the builder run if the length of
		*!*		the DeleteCmd syntax exceeds 254 characters, 
		*!*	
		*!*		Changed a line of code in the Cr8_ADOCode method:
		*!*			From:	IF NOT PEMSTATUS(THIS.oADO_Conn, [Provider], 5)
		*!*			To:		IF TYPE("THIS.oADO_Conn.Provider") <> "C"
		*!* 	because the PEMSTATUS function was incorrectly evaluating to FALSE
		*!*		the first time that ADO object property was hit. Thereafter, it
		*!*		would correctly evaluate to TRUE. Changing to the TYPE function
		*!*		corrects the problem with the first hit.
		*!*	
		*!*	Version 2.05.00 [August 2003]
		*!*	
		*!*		Added a new method, _BuildSelectCmd, that creates the SQL for
		*!*		the SelectCmd property based on the cBaseSelectCmd [new property],
		*!*		cSelectCmdFilter, cSelectOrderBy and cSelectOtherClauses properties.
		*!*	
		*!*		Added Assign methods for the cBaseSelectCmd, cSelectCmdFilter,
		*!*		cSelectOrderBy and cSelectOtherClauses properties that call the
		*!*		_BuildSelectCmd method when any of these property values changes.
		*!*	
		*!*	Version 2.04.00 [July 2003]
		*!*	
		*!*		Modified how the CursorSchema is created for Oracle tables with
		*!*		respect to how numeric columns are sized. VFP actually defines
		*!*		the width 2 digits wider that what is defined in the Oracle table.
		*!*		There are more comments on this in the Cr8_CursorSchema method.
		*!*	
		*!*		Added another configurable option to the builder form so you can
		*!*		remap Number columns of specific widths as Integers in the
		*!*		CursorSchema.
		*!*	
		*!*		The FetchSize property of the CA base class is now set to -1.
		*!*	
		*!*	Version 2.03.00 [June 2003]
		*!*	
		*!*		Added an option on the builder form to use DATE data types
		*!*		in the CursorSchema for any field/column that is a DATETIME
		*!*		data type in the source table.
		*!*	
		*!*		Added 2 public properties to the CA Base Class --
		*!*	
		*!*			cSelectOrderBy
		*!*				Optional ORDER BY clause to be added to the SelectCmd.
		*!*				Do NOT include the words ORDER BY in your list.
		*!*	
		*!*			cSelectOtherClauses
		*!*				Optional clause(s) to be added to the SelectCmd AFTER
		*!*				the ORDER BY clause (e.g., GROUP BY and HAVING). You
		*!*				will need to include those key phrases in your string.
		*!*	
		*!*		Added public method to the CA Base Class --
		*!*	
		*!*			_AfterCursorFillRefresh
		*!*				Fired by the AfterCursorFill and AfterCursorRefresh events
		*!*				using BINDEVENT. This method is useful for placing code
		*!*				to create indexes on the cursor after the cursor is
		*!*				created by the CursorAdapter.
		
		* * * * * *   P R O P E R T Y   S H E E T   G O T C H A   * * * * * *
		
		*!*	There is a known issue with a limitation in the Property sheet
		*!*	which only allows the display of a maximum of 255 characters.
		*!*	This is particularly annoying for properties like CursorSchema
		*!*	because without information in the CursorSchema property, you
		*!*	can not drag fields from the cursor and drop them onto a form.
		*!*	
		*!*	What this builder will do to partially alleviate this problem
		*!*	problem is set the CursorSchema property to as many fields as
		*!*	possible without exceeding the limit. If this is an incomplete
		*!*	schema, the lSchemaTruncated property will be set to TRUE. At
		*!*	runtime, the entire schema will be programmatically inserted
		*!*	into the CursorSchema property when the _CA_INIT method fires.
		
		* * * * * *   O R A C L E   G O T C H A   * * * * * *
		
		*!*	If you have an Oracle table with a column that is defined
		*!*	as a NUMBER data type without specifying a width or has a
		*!*	specified width greater than 20, VFP could return an error
		*!*	when the CursorAdapter's CursorFill fires. This would be
		*!*	Error #9 -- Data Type Mismatch.
		
		* * * * * *   S Q L   S E R V E R   G O T C H A   * * * * * *
		
		*!*	If using ADO, there is a problem using a 
		*!*	parameterized query on an NCHAR data type. A SelectCmd like
		*!*	
		*!*	cCustomerID = [M%]
		*!*	select * from Customers where CustomerID like ?cCustomerID
		*!*	
		*!*	will return no records because in the Northwind database where
		*!*	this example was tested against, the CustomerID is a NCHAR(5)
		*!*	data type. When the SQL is passed to the server, the parameter
		*!*	value of 'M%' gets RPADded with spaces to make the parameter 
		*!*	value a width to match the field width in the table. In other
		*!*	words the values is passed as 'M%   '. This does not happen
		*!*	with NVARCHAR datatypes. The workaround is to create your SQL as:
		*!*	
		*!*	cCustomerID = [M%]
		*!*	select * from Customers where CustomerID like RTRIM(?cCustomerID)
		
		* * * * * *   W A R N I N G  * * * * * *
		
		*!*	Do NOT run BEAUTIFY on this PRG file!
		*!*	
		*!*	Many of the methods use TEXT TO MemVar NOSHOW/ENDTEXT to create
		*!*	code snippets using the PRETEXT option to remove tabs and/or
		*!*	spaces at the beginning of each line of code. Beautify could
		*!*	convert these spaces to tabs and make the code written into the
		*!*	methods of the Classes to be structured oddly. This in no way
		*!*	affects the execution of the code. The effect is only cosmetic.
		
		* * * * * *   W A R N I N G   * * * * * *
		
		*!*	Table names that include spaces are an EXTREMELY bad idea.
		*!*	If you run this builder against the sample Northwind database in
		*!*	MSDE, the "Order Details" table will NOT be processed because of
		*!*	the space in the name. Not only is this table name bad, it includes
		*!*	the key word "ORDER" which the SQL parser could confuse as part of
		*!*	a poorly formed ORDER BY clause. This same warning applies to the
		*!*	defining of column/field names. Thanks go to the Northwind database
		*!*	designers at Microsoft for giving us such a fine example of what not
		*!*	to do when coming up with table names. This probably came from
		*!*	someone who created the original database in <gasp!> MS Access. <bg>
		*!*	Thanks to the MS VFP Team for converting it to a VFP database
		*!*	without spaces in the table names.
		
		* * * * * *   F U T U R E   P L A N S   F O R   T H I S   B U I L D E R   * * * * * *
		
		*!*	The next planned MAJOR enhancement is to add methods and properties
		*!*	that will handle the passing of values from the curor's fields to
		*!*	stored procedures in SQL Server and Oracle. The SPs that I am referring
		*!*	to are those that are explicitly designed to handle all SELECT, INSERT,
		*!*	UPDATE and DELETE SQL functions.
		
		* * * * * *   U S I N G   T H E   V C X   C R E A T E D   B Y   T H I S   B U I L D E R   * * * * * *
		
		*!*	One of the purposes of this builder is to minimize the amount of
		*!*	work you need to do to design a CA for each table in your database.
		*!*	I do not know of any builder that will create something to handle
		*!*	every situation or requirement a developer may encounter.
		*!*	
		*!*	With that said...
		*!*	
		*!*	Once you have used this builder to create the class library of
		*!*	CursorAdapters, you should look at the code in the various methods
		*!*	of the CA baseclass AND subclasses to see what needs to be tweeked.
		*!*	Note the syntax of the *Cmd properties. You will be responsible for
		*!*	initializing any parameters used in the *Cmd properties.
		*!*	
		*!*	For convenience, I have included the PK fields in the InsertCmd and
		*!*	UpdateCmd properties if you are not going to use the Auto-Update
		*!*	feature of the CA. I use Insert triggers to automatically populate
		*!*	my PK fields for new records. I also do not update the PK in my
		*!*	Update SQL commands. So, I suggest you delete the PK fields from
		*!*	the InsertCmd and UpdateCmd properties and allow your database to
		*!*	handle PK generation or use the new Auto Increment feature on your
		*!*	NATIVE tables.
		*!*	
		*!*	You should now be ready to create a form and add any of the CAs
		*!*	to the DataEnvironment using the DE Builder that ships with VFP8.
		*!*	Do not add any tables to the DE. In the blank DE, right-click and
		*!*	select the Builder... option. Click on the second tab, "2. Cursors"
		*!*	and click the Add... button. Find the VCX you just created with this
		*!*	builder, then select the CA from the VCX you wish to add. Edit the
		*!*	Name and Alias properties in the builder if needed. Add other CAs
		*!*	as needed then close the builder.
		*!*	
		*!*	All that is left for you to do is either set the DataSource and
		*!*	DataSourceType properties. Since this will most likely be the same
		*!*	for all CAs, you can set these properties in the CA baseclass and
		*!*	all the CA subclasses will use these same settings. Cool, eh?
		*!*	If you are accessing NATIVE data, all you should have to do is
		*!*	SET DATABASE TO MYDBC in the BeforeOpenTables method of the DE as
		*!*	well as other SET commands like TALK and DELETED if you are using
		*!*	private datasessions [which you should be].
		*!*	
		*!*	There are 5 custom propertes added to the CA baseclass that can be
		*!*	set at the baseclass level or for each individual CA subclass. The
		*!*	lUseCursorSchema, lNoDataOnLoad and nFillOptions properties correspond
		*!*	to the first 3 parameters accepted by the CursorFill method.
		*!*	
		*!*		lUseCursorSchema
		*!*			Set to TRUE if you want the CursorFill method to
		*!*			use the structure in the CursorSchema property.
		*!*			The default is FALSE.
		*!*	
		*!*		lNoDataOnLoad
		*!*			Set to TRUE if you want the cursor to be created with NO DATA
		*!*			returned from the source table[s]. The default is FALSE.
		*!*	
		*!*		nFillOptions
		*!*			See VFP Help on CursorFill for discussion on the use of nOptions.
		*!*			The default is -1 but set to 0 in the CursorFill method if the
		*!*			DataSourceType is not ADO.
		*!*	
		*!*		lAutoGenerate
		*!*			The setting of this property determines how the *Cmd properties
		*!*			are populated when the CA is instanciated. If this property is
		*!*			TRUE, the value of the lCallStoredProcs is ignored, and the
		*!*			values of the *Cmd properties will be set by the code in the
		*!*			_CA_AutoUpdate method of each CA subclass. If this property is
		*!*			FALSE, the values of the *Cmd based on the value of the
		*!*			lCallStoredProcs property.
		*!*	
		*!*		lCallStoredProcs
		*!*			If this is TRUE, the values of the *Cmd properties will be
		*!*			set by the code in the _CA_SPCode method of each CA subclass.
		*!*			Otherwise, the *Cmd properties will be set by the code in the
		*!*			_CA_DMLCode method.
		*!*	
		*!*
		*!*	Thanks and Good Luck,
		*!*	
		*!*	Mark E. McCasland
		*!*	M & J Software
		*!*	http://www.mctweedle.com
		
	ENDPROC

ENDDEFINE
