The Farber
Consulting Group, Inc. |
Tel: (516) 796-6545
/ Fax:
(516) 796-1273
E-Mail Me: doron@dfarber.com
Our Web Site: http://www.dfarber.com
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 mig ht 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 oneI 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
Page 2
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-codedforeign
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
Page 3
Init Method
*************************************************************************************
*** . . .Procedure. . . . . . . . . .. : Init
*** . . .Author . . . . . . . . . . . . .. : Doron Farber,
E-MAIL:doron@dfarber.com
*** . . . . . . . . . . . . . . . . . . . . . . . : Tel:
516-796-6545
*** . . .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 chararters?
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
Page 4
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
*** . . .CopyRight . . . . . . . . . . ..: 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.