Generic Processing, Continued
May 16, 2012 Ted Holt
Imagine needing to delete a large number–50, 100, 1,000–device descriptions, all of which begin with the same group of characters, from your system. Now imagine the Delete Device Description (DLTDEVD) command not accepting a generic device name. That got ugly quickly, didn’t it? And yet that sort of thing is what programmers often force end users to do. Maybe we need to add generic processing to our applications.
I have written about generic names before. (See Related Stories at the end of this article.) But I have not written about using generic values in the context of application development.
Suppose a group of users needs to tell the system that certain things are to be treated special in some way. Since I work in a manufacturing environment, I’ll use inventory items as an example. I could create a database file into which they could load the item numbers. I could call it EXCPTLIST, for exception list, and it might look like this:
A UNIQUE A R EXCEPREC A ACTIVE 1 TEXT('A=ACTIVE, I=INACTIVE') A ITEMNBR 12A A K ITEMNBR
The users could easily manage a small number of items. But what if they had many items? That could be a problem.
One way to make their life easier is to allow them to use generic item numbers (e.g., BR549*.) But to do so, we have to do a little extra work.
Here’s a simple program that illustrates the concept. SearchItem is an item number, not a generic value. I want to know if that item number matches an active value in the exception list.
H option(*srcstmt: *nodebugio) H dftactgrp(*no) actgrp(*caller) FExcepList if e k disk usropn D SearchItem s 12a D MatchFound s n D CheckGeneric pr D inValue 12a const D ouMatchFound n D CheckValue pr D inValue 12a const D ouMatchFound n /free *inlr = *on; open ExcepList; CheckValue (SearchItem: MatchFound); if not MatchFound; CheckGeneric (SearchItem: MatchFound); endif; if MatchFound; // do something else; // do something else endif; return; /end-free * ================================================ P CheckGeneric b D pi D inValue 12a const D ouMatchFound n D*** locals D WrkValue s like(inValue) D ValueSize c const(%size(WrkValue)) D Ndx s 10i 0 /free WrkValue = inValue; ouMatchFound = *off; Ndx = %len(%trimr(WrkValue))+1; if Ndx > ValueSize; Ndx = ValueSize; endif; dow (not ouMatchFound) and (Ndx >= 2); %subst(WrkValue: Ndx) = '*'; CheckValue (WrkValue: ouMatchFound); Ndx -= 1; enddo; /end-free P e * ========================================================= P CheckValue b D pi D inValue 12a const D ouMatchFound n /free chain (inValue) ExcepRec; ouMatchFound = %found() and Active = 'A'; /end-free P e
After opening the file, the call to CheckValue looks for an exact match in the exception list. If SearchItem is BR549, then there must be a record with an activity code of A and an item number of BR549 in order for MatchFound to be set to true.
If no match is found for the exact value, the program searches for a generic value. If the search value has no trailing blanks, the first search is for a value that replaces the last character with an asterisk. That is, for item AAABBBCCCDDD, the routine begins looking for AAABBBCCCDD*. But if the search item has trailing blanks, then the search begins by adding an asterisk to the end of the search item. That is, the search for item XXYYZZ begins by looking for an active record with the value XXYYZZ*.
CheckGenericValue works from the end of the string toward the front, moving the asterisk one place to the left each time. Searching for a match for BR549 means that the loop checks BR549*, BR54*, BR5*, BR*, and finally B*, until it finds a match in the database or has backed the asterisk to the second position.
Matching generic values requires more code than the simple CHAIN and IF that would be required to match exact values only. But the good thing is that these routines are easily cloned. I know, because I cloned them from a generic search routine that I wrote recently.