Dev-Picayune

picayune: of little value or importance

A Clarion Export Template

February 03, 2006

I’ve done some Clarion Templates before, but I hadn’t tried my hand at creating something that I always thought would be useful and that is a template for exporting files to text (delimited) files. Obviously, the best solution would have been an entire application template that would generate all the procedures for all the files and the contol processes so that all you had to do was create a new .APP, but that was a little out of my budgeted time (1 day +/- a little).

The goal was this:

Create a command-line utility that can export any one of several specific files (out of maybe 100+) in the dictionary to a ‘~’ delimited file.

My first task was deciding how to actually export the files. As it turns out, one of the files requested was not in the dictionary so I had to hand code a procedure for that one file but for the other files, I knew that a template could be useful. Knowing that I might later have to be able to export other files, I though making the template would still be worth the trouble. Unlike other languages that have more introspection capabilities, Clarion doesn’t seem to have any real good run-time mechanism for iterating through it’s own structures. So I decided that a template would be my best course of action.

My second task was actually making my Clarion application act like a command-line utility (i.e. not starting a seperate windows thread but really running on the Console). For that I ended up choosing to create the Clarion portion as a .DLL and then calling the .DLL from Python. For that part, you’ll have to refer to my other article on Calling Clarion from Python.

The Template

To save time, I chose to just copy the source code procedure template that was already in place. Minus a few special embeds specific to the template we were using, here is the basic template I created (contents of exportfi.tpw):

 
#PROCEDURE (ExportFile, 'Export File'), PRIMARY('File to Export')
#PROMPT ('Parameters', @s255), %Parameters
#PROMPT ('Separator',@s3),%ExSep
#IF (%Parameters)          
%[20]Procedure %ProcedureType %Parameters
#ELSE
%[20]Procedure %ProcedureType
#ENDIF
#INSERT (%FileControlInitialize)
#AT (%CustomGlobalDeclarations)
  #INSERT (%FileControlSetFlags)
#ENDAT
 
#FOR (%LocalData)
%[20]LocalData %LocalDataStatement
#ENDFOR
#EMBED (%DataSection, 'Data Section'), DATA
OutputFile      FILE,DRIVER('ASCII','/ENDOFRECORD=1,13'),CREATE
                  RECORD
OutputBuffer        STRING(65000)
                  END
                END
 
  CODE
  !scw - super duper export code goes here... 
  #INSERT(%FileControlOpen)
  #FOR (%ProcFilesUsed)
    #FIX (%File, %ProcFilesUsed)
    OutputFile{PROP:Name} = ExportName
    CREATE(OutputFile)
    IF ERRORCODE()
      RETURN 2
    END
 
    OutputFile{PROP:Name} = ExportName
    OPEN(OutputFile,ReadWrite+DenyNone)
 
    IF ERRORCODE()
      RETURN 2
    END
 
    SET(OutputFile)
    IF ERRORCODE()
      CLOSE(OutputFile)
      RETURN 2
    END
 
    SET(%File)
    IF ERRORCODE()
      CLOSE(OutputFile)
      RETURN 3
    END
 
    LOOP
      NEXT(%File)
      IF ERRORCODE() THEN BREAK.
      OutputFile.OutputBuffer = ''
    #FOR(%Field)
      #CASE(%FieldType)
      #OF ('STRING')
      OutputFile.OutputBuffer = CLIP(OutputFile.OutputBuffer) & '%ExSep' & |
                                CLIP(StripString(%FieldFile.%FieldID))
      #OF ('LONG')
      #OROF ('SHORT')
      #OROF ('DECIMAL')
      #OROF ('BYTE')
      #OROF ('REAL')
      #OROF ('USHORT')
      #OROF ('ULONG')
      OutputFile.OutputBuffer = CLIP(OutputFile.OutputBuffer) & '%ExSep' & |
                                CLIP(FORMAT(%FieldFile.%FieldID,%FieldDisplayPicture))
      #ENDCASE
     #ENDFOR
      OutputFile.OutputBuffer = SUB(OutputFile.OutputBuffer,2,| 
                                LEN(CLIP(OutputFile.OutputBuffer)) - 1)
      APPEND(OutputFile)
    END
 
   #ENDFOR
   CLOSE(OutputFile)
  #EMBED (%ProcessedCode, 'Processed Code')
  #INSERT(%FileControlClose)
  #EMBED (%AfterClosingFiles, 'After Closing Files')
  RETURN(0)

The really important part of this template is the key to creating an export template:

    #FOR(%Field)

This really acts like a for-each type of statement that generates the code inside the #FOR for each field in the current %File.
For setting the %File correctly, I use:

  #FOR (%ProcFilesUsed)
    #FIX (%File, %ProcFilesUsed)

This implies that I should make sure I only setup 1 file in the “Files” Section of the procedure.
Some other assumptions are also made:

  • That a procedure named StripString() exists and takes a STRING as input and returns a STRING (this is used to remove any delimiters or special characters)
  • That the procedure will have a STRING parameter named “ExportName” that is used to create the export file
  • That a valid entry has been made by the user in the new %ExSep value (the delimiter)
  • That the desired output picture has been setup already in the dictionary for each field
  • That the procedure prototype has been setup to return a BYTE

Yes, I realize those are a lot of assumptions, but what do you expect for free here? I am just trying to get your started on this.

In order to use the template, you’ll also need a .tpl file to actually register the template:

#TEMPLATE(ExportData,'Stevens Export Template')
#INCLUDE('filectrl.tpw')
#INCLUDE('exportfi.tpw')

Once you have those two files created in your TEMPLATE directory you can then register them and start created procedures from it.

In the end, after creating some procedures based on my template, and then creating a procedure to call the other procedures, my hierarchy looked like this:

  • Main (To-Do)
  • ExportController
    • ExportCUSTOMERS
    • ExportADDRESSES
    • ExportPHONES
    • ExportCONFIG
  • StripString

For the StripString function I used the following bit of code (albeit imperfect) that I through together to remove the ‘~’ that I would be using for delimiting my fields and the carriage-returns and line-feeds that obviously would also mess up my text output file (customize obviously as needed):

StripString          FUNCTION (inputstring)
LOC:OutputString    STRING(1024)
  CODE
  LOC:OutputString = CLIP(InputString)
  X# = INSTRING('~',CLIP(LOC:OutputString),1,1)
  LOOP WHILE X# > 0
    LOC:OutputString[X#] = '`'
    X# = INSTRING('~',CLIP(LOC:OutputString),1,1)
  END
  X# = INSTRING(chr(10),CLIP(LOC:OutputString),1,1)
  LOOP WHILE X# > 0
    LOC:OutputString[X#] = ' '
    X# = INSTRING(chr(10),CLIP(LOC:OutputString),1,1)
  END
  X# = INSTRING(chr(13),CLIP(LOC:OutputString),1,1)
  LOOP WHILE X# > 0
    LOC:OutputString[X#] = ' '
    X# = INSTRING(chr(13),CLIP(LOC:OutputString),1,1)
  END

  RETURN CLIP(LOC:OutputString)

Then for the actual ExportController function I did something like this:

ExportController     FUNCTION (lptrTable,lptrExport)
FileToConvert  CSTRING(20)
ExportName  CSTRING(255)
LOC:File    STRING(20)
LOC:ExportName  STRING(255)
  CODE
  lstrcpy(ADDRESS(FileToConvert),lptrTable)
  lstrcpy(ADDRESS(ExportName),lptrExport)
  LOC:File = CLIP(FileToConvert)
  LOC:ExportName = CLIP(ExportName)
  GLO:CUSTOMER = GETINI('Files','CUSTOMERS','\data\cust.tps','.\exportfi.ini')
  GLO:ADDRESSES = GETINI('Files','ADDRESSES','\data\address.tps','.\exportfi.ini')
  GLO:PHONES = GETINI('Files','PHONES','\data\phones.tps','.\exportfi.ini')
  GLO:CONFIG = GETINI('Files','CONFIG','\data\config.tps','.\exportfi.ini')
  CASE CLIP(UPPER(LOC:File))
  OF 'CUSTOMERS'
    RETURN(ExportCUSTOMERS(LOC:ExportName))
  OF 'ADDRESSES'
    RETURN(ExportADDRESSES(LOC:ExportName))
  OF 'PHONES'
    RETURN(ExportPHONES(LOC:ExportName))
  OF 'CONFIG'
    RETURN(ExportCONFIG(LOC:ExportName))
  ELSE
    RETURN(1)
  END

Now obviously in my example, the dictionary was specifying some Global fields for use as the filename for each file. So I used a new .INI file and GETINI to set the actual filenames. If your dictionary hardcodes the filenames, you won’t have to do this.

Also of note here is that I am doing some special conversion of the input parameters to the function. This is due to the fact that I am going to be calling this function from outside Clarion (I’ll be discussing that part in my article on Calling Clarion from Python).

Hopefully this is enough to get you started on writing your own file export template that you should hopefully be able to make even more automated than mine. Enjoy!

No comments yet. Be the first.

Leave a reply

For spam filtering purposes, please copy the number 4171 to the field below: