|
|
|
|
|
|
|
Padding The Key Field Just Got Better
|
|
Part of the problem with padding a key field is
that, in some cases, it could be alphanumeric. For example, a key field
might be 12 characters long but also be required to allow the entry of
values such as PROJECT00001 or 000400. In this article, Doron describes
a class that resolves this problem.
|
I was faced with a problem when my client requested that I allow the
user to enter alphanumeric characters in the key field. It got even
better when I had to pad the string only if the user entered a string
consisting only of numeric characters.
If the user entered an alphanumeric combination, there was no need to
pad the key field. Of course, it's required to check whether it's a
unique number, but that's beside the point. At the beginning, the
function determines the length of the field and the number of characters
that were entered. It also checks whether an object or a MemVar was
passed.
Since I might not need to pad the object directly in some cases, a text
box is an object in this case. At this point, I was wondering if there's
a native function in VFP that could tell me whether there's a character
within a particular string. ISALPHA() can find only the left- most alpha
character, but what happens if the key field is something like 55UA78?
In this case, ISALPHA() wouldn't find the alpha character.
I thought that there might have been a hidden function that's yet to be
discovered, but no such luck. I went to CompuServe, thinking that maybe
somebody would direct me to such a function. One suggestion was the
expression shown in Listing 1.
|
Listing 1. Using the CHRTRAN function to parse a string. IF LEN(CHRTRAN(UPPER(m.lcNewValue) , ;
'"'+"ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+`-=|}{[]|;:?/>.<, ' ",""))
<> LEN(m.lcNewValue)
m.llIsAlpah=.T.
ENDIF
I didn't think that the preceding solution was the best one—I suspected
it would be faster to do it with ISALPHA(SUBSTR(m.lcNewValue,m.nJ,1))
and enclose that expression within a FOR..ENDFOR loop.
I decided at first to use the following LOOP, with
ISALPHA(SUBSTR(m.lcNewValue,m.nJ,1)), and enclose this expression within
a FOR..ENDFOR loop (see Listing 2). The number of loops depends on the
length of the field. Once an alpha character is found, the loop is
terminated.
To determine which expression is faster, I tested both within a field
that contained the value "123456789012345s".
|
Listing 2. Using a loop to parse a string.
SET FIXED ON
SET DECIMALS TO 18
m.Start=SECONDS()
FOR m.nJ=1 TO m.nlNumOfChr
IF ISALPHA(SUBSTR(m.lcNewValue,m.nJ,1))
m.llIsAlpha=.T.
EXIT
ENDIF
ENDFOR
m.End=SECONDS()
m.Total= m.End- m.Start
|
Test results Repeated tests of the code shown in Listing 2 took either
0.0009999999999 (1 /10000 of a second) or 0.0000000000. The code in
Listing 1 showed all times as 0.00000000000000000 seconds. Since the
field had 16 characters and only the last one was an alpha, 16 loops
were executed to determine whether there was a
character within the string. With a string of 30 characters, the results
were the same amount of time.
The only problem I found with Listing 1 is that the string is hard-coded
foreign characters aren't included. I decided to use the FOR..ENDFOR
expression to be on the safe side.
The flag m.llIsAlpha is assigned a .T. value when there's an alpha
character within the m.lcNewValue string, and thus there's no need to
pad that string. If no alpha character is found, then I need to check
whether the number of characters entered was equal to the field's
length.
At the beginning of that class, I determine whether this is an object or
just a MemVar that's to be processed, and set m.llIsObject to indicate
so. If it's .T., then it's an object; if .F., it's a MemVar.
IF TYPE('oKeyFld')=="O"
m.nlFldLen=LEN(oKeyFld.Value)
m.lcNewValue=ALLTRIM(oKeyFld.Value)
m.llIsObject=.T.
ELSE
m.nlFldLen=LEN(oKeyFld)
m.lcNewValue=ALLTRIM(oKeyFld)
m.llIsObject=.F.
ENDIF
In addition the class determines if the m.lcNewValue value was zero or a
negative number The following is the code:
IF VAL(m.lcNewValue)<=0
IF m.llIsObject
oKeyFld.Value=This.DisplayMessage(m.nlFldLen)
This.llIsEmpty=.T.
ELSE
This.lcRetValue=This.DisplayMessage(m.nlFldLen)
ENDIF
ELSE
IF ! m.llIsObject
This.lcRetValue=m.lcNewValue
ENDIF
ENDIF
RETURN
|
Init Method ********************************************************************************
*** Procedure: Init
*** Author: Doron Farber
*** Tel: 732-536-4765
*** Created: Feb 12, 1998
*** Copyright: (c) The Farber Consulting Group, Inc.
*** Purpose: This class can directly pad the current text box, and check
if numeric
*** characters are less then the field length. If the value is zero it
pops up
*** a message and return a space in the field's length. The developer
may
*** check IF ! EMPTY(). It also checks if one alpha character included
in
*** the text box, if found do not pad the field, leave it as is. In most
cases
*** it should be called from the TextBox valid method.
*** Parameters: oKeyFld - An object reference to current active text
box, or MemVar value.
*** MemVars: m.lcIsAlpah - A flag whether an alpha character is included
in a MemVar or This.Value
*** m.nlNumOfChr - Tell us how many characters were entered into the
field.
*** m.nlFldLen - Tell us the field length.
*** m.J - A Counter for the FOR .. ENDFOR loop.
*** m.lcNewValue - Current trimmed value entered into the field.
*** Properties: lcRetVlaue - Gets its value when a memvar is passed,
this memver
*** could be the text box value.
*** llIsEmpty - Gives an indication if the textbox is empty.
*** Calling: a) oPadKeyFld=CREATEOBJECT("PadKeyFld",This) Modify the
text box.
*** b) oPadKeyFld=CREATEOBJECT("PadKeyFld",m.NowValue)
*** Returns value, When m.NowValue could be textbox value or any MemVar.
*** Return: oPadKeyFld.lcRetValue property, is used only for a NemVar as
a
*** return value, m.lcNewValue=oPadKeyFld.lcRetValue
*** oPadKeyFld.llIsEmpty - When the textbox is empty it returns .T.
*** Notes: Use calling item (b) only if you do not wish to modify the
text box directly.
*******************************************************************************
LPARAMETERS oKeyFld
LOCAL m.llIsAlpah,m.nlNumOfChr,m.nlFldLen,m.nJ,m.lcNewValue,m.llIsObject
m.llIsAlpah=.F.
This.llIsEmpty=.F.
IF TYPE('oKeyFld')=="O"
m.nlFldLen=LEN(oKeyFld.Value)
m.lcNewValue=ALLTRIM(oKeyFld.Value)
m.llIsObject=.T.
ELSE
m.nlFldLen=LEN(oKeyFld)
m.lcNewValue=ALLTRIM(oKeyFld)
m.llIsObject=.F.
ENDIF
m.nlNumOfChr=LEN(m.lcNewValue)
*** Is any Alpha character included ??
FOR m.nJ=1 TO m.nlNumOfChr
IF ISALPHA(SUBSTR(m.lcNewValue,m.nJ,m.nJ))
m.llIsAlpah=.T.
EXIT
ENDIF
ENDFOR
*** Are number of characters smaller then text box length
IF m.nlNumOfChr<m.nlFldLen
*** Is it numeric
IF ! m.llIsAlpah
*** Is it a memvar or an object
IF m.llIsObject
m.lcNewValue=PADL(m.lcNewValue,m.nlFldLen,"0")
*** Make sure key value is greater then zero, if Zero return blank field
with spaces
IF VAL(m.lcNewValue)>0
*** Change the value of the object
oKeyFld.Value=m.lcNewValue
RETURN
ENDIF
ELSE && IF m.llIsObject
*** So, it is not an object it is a MemVer
m.lcNewValue=PADL(m.lcNewValue,m.nlFldLen,"0")
IF VAL(m.lcNewValue)>0
This.lcRetValue=m.lcNewValue
RETURN
ENDIF && IF VAL(m.lcNewValue)>0
ENDIF && IF m.llIsObject
ELSE
*** It has an alpha character nothing to do
This.lcRetValue=m.lcNewValue
RETURN
ENDIF && IF ! m.lcIsAlpah
ELSE && IF m.nlNumOfChr<m.nlFldLen
** Does it have the maximum required characters?
But make sure it has at
** least one alpha character, so it will not be
considered as zero. Since
** VAL(A22) or VAL(Z44) will produce a Zero, no
matter which alpha
** character will be in front
IF m.llIsAlpah
IF ! m.llIsObject
This.lcRetValue=m.lcNewValue
ENDIF
*** If it is an object do nothing on the text box
RETURN
ENDIF
ENDIF && IF m.nlNumOfChr<m.nlFldLen
*** Make sure key value is greater then zero, if Zero VAL(m.lcNewValue)<=0
return
*** blank field with spaces. This is occurs when either m.nlNumOfChr==m.nlFldLen
*** or m.nlNumOfChr<m.nlFldLen, or it is a MemVer
IF VAL(m.lcNewValue)<=0
IF m.llIsObject
oKeyFld.Value=This.DisplayMessage(m.nlFldLen)
This.llIsEmpty=.T.
ELSE
This.lcRetValue=This.DisplayMessage(m.nlFldLen)
ENDIF
ELSE
IF ! m.llIsObject
This.lcRetValue=m.lcNewValue
ENDIF
ENDIF
RETURN
|
DisplayMessage Method
The Display Message method is called from the init(0 method and provides
an error message when the value of the key field is zero or negative
number.
*******************************************************************************
*** Program: DisplayMessage
*** Copy Right: The Farber Consulting Group, Inc.
*** Author: The Farber Consulting Group Inc. By Doron Farber
*** Purpose: To display an error message when no value entered, or not
*** greater then zero.
*** Parameters: m.nlFldLen - Gets the length of either, the text box or
memver
*** MemVars: m.lcMessageTitle - Holds the message title
*** m.lcMessageText - Holds the actual message
*** Calling: It is called from the init method of this class.
*** Return: SPACE(m.nlFldLen)
*** Notes: None
*******************************************************************************
LPARAMETER m.tnlFldLen
LOCAL m.lcMessageTitle,m.lcMessageText
m.lcMessageTitle = 'Key Field Is Zero!!
m.lcMessageText = 'You Must Enter A Value Greater Then Zero'
*** ** MB_OK+MB_ICONSTOP ******
=MESSAGEBOX(m.lcMessageText, 0 + 16, m.lcMessageTitle)
RETURN SPACE(m.tnlFldLen)
When the key value is zero or negative, DisplayMessage returns a string
of
spaces equal to the length of the field. This class can be called in one
of two ways.
Both are fired from the text box valid method.
If the text box is empty, the class will display a message to alert the
user.
Therefore, just RETURN 0 from the valid method. llIsEmpty will be .T.
the
following is sample code to be placed within the text box valid method:
LOCAL oPadKeyFld
oPadKeyFld=CREATEOBJECT("PadKeyFld",This)
IF oPadKeyFld.llIsEmpty
RETURN 0
ELSE
RETURN
ENDIF
You can also call it from the valid method but have changes performed
indirectly on the text box. The following is sample code to be placed
within
the text box valid method:
LOCAL m.lcNewValue, oPadKeyFld
m.lcNewValue=This.Value
oPadKeyFld=CREATEOBJECT("PadKeyFld",m.lcNewValue)
IF EMPTY(oPadKeyFld.lcRetValue)
RETURN 0
ELSE
This.Value=oPadKeyFld.lcRetValue
RETURN
ENDIF
|
In the main program of your application, enter the following line of
code:
SET CLASSLIB TO FcgUtils ADDITIVE
Please look into the sample form named PadKey. I have two samples of
code within the valid method of each field. The full source code of the
preceding function is included with the Subscriber Downloads at
http://www.pinpub.com/foxtalk. In its simplest form, you'd just plug
oPadKeyFld=CREATEOBJECT("PadKeyFld",This) within the valid method of the
text box object, and it will just kick in.
|
|
|
|
|
|
|
|