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 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

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-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

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.