Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Adding Power to PowerBuilder


Visual Tools 96: Adding Power to Powerbuilder

Adding Power to PowerBuilder

An awk-like user object for string parsing

Baylor Wetzel

Baylor is a client/server developer for Born Information Services Group. He can be contacted at baylor.wetzel @born.com.


PowerBuilder is a visual-development tool for creating client/server applications. Although it is cross platformsupporting Windows 3.1/95/NT, Macintosh, and UNIXPowerBuilder, nevertheless, does not offer the wide assortment of tools that UNIX developers have come to rely upon. For instance, one of the most common (and useful) UNIX tools is awk, a programming language used to parse large numbers of strings. An awk program such as { if ($1 == "Hello") print $0 }, for example, says that if the first word of the line is "Hello" ($1), then print the entire line ($0). In awk, the $ represents a field in the current line, with field 0 meaning the whole line. Thus, in the sentence "Hello World, I'm written in awk", $1 is "Hello," $2 is "World," all the way through $6, "awk."

Awk can also tell you how many words were in a line ($nf) and the current line number ($nr). Awk can also tell where one word ends and another begins (usually by looking for a space, tab, or comma) with $fs, the field separator.

Awk has three basic events: Begin, Scan, and End. Code in Begin occurs before the file ($FileName) is processed, Scan occurs as each line is read, and End occurs when all the records have been read. When Scan is triggered, the current line will have been parsed into a number of fields ($1 to $nf). This is where you can look for useful things, such as the number of lines that contain "EXE" in field two, or contain the word "Asia" in field three.

Awk does exist for the PC platform, but integrating it with PowerBuilder isn't easy. In this article, I'll present an awk-like nonvisual user object that can do the same things awk can do, but with the level of integration with databases and graphical objects that UNIX programmers dream about. Implementation of the awk user object for PowerBuilder 4.x and 5.0 are available electronically.

PowerBuilder Objects

There are two basic types of PowerBuilder objects: visual and nonvisual. There are several types of visual objects, from standard controls (in which you subclass a standard PowerBuilder control, such as a button or listbox) to custom objects (a collection of standard and other custom visual objects).

Nonvisual objects, on the other hand, are the equivalent of a class in many other languages. Nonvisual objects have three items of interestvariables, functions, and events. In PowerBuilder, you can define the values of those variables as unique to that instance, or shared across all instances. You can also declare them as public, private, or protected.

In PowerBuilder 4.x, a function was like the standard function/method found in most languages, complete with parameters and return values. Events allowed PowerBuilder to fit into Windows' asynchronous, message-driven paradigm. Up through PowerBuilder 4.x, events did not take parameters and did not have return codes.

In PowerBuilder 5.0, events take parameters and have return codes, just like functions, and functions can be called synchronously (triggered) or asynchronously (posted), just like events.

There are now very few differences between PowerBuilder functions and events. For the record, PowerBuilder events are cumulative, meaning that, unless you explicitly override the ancestor's event, executing your event executes the ancestor's code, too (the ancestor's code always runs first). In addition, events act like they have a virtual stub. With functions, you need to define the function in the class or an ancestor of that class, or risk the compiler's wrath. With events, you can write code to execute an event, whether the event exists or not, without penalty at compile or run time.

Building the Awk Object

The first step to building an awk object is to go into the User Object Painter, select New (see Figure 1), and select the Nonvisual user-object option. You'll get a blank square on the screen. With PowerBuilder's nonvisual objects, the object being displayed will never show up in a program; it is only there so you can click on it and enter script for the events. You can then add events by selecting Declare/User Events.

For the awk object, I'll start by adding four eventsue_begin ( pbm_custom01), ue_scan (pbm_custom02), ue_end (pbm_custom03), and ue_error (pbm_custom04); see Figure 2. (ue_ stands for "user events").

It's a good idea to have naming standards to differentiate your events (and functions and controls) from PowerBuilder's. Also, you will avoid problems when naming an event "end," which is a PowerBuilder-reserved word.

You'll also need instance variables, so go to the Instance Variable Painter and enter Listing One); for functions, create Listing Two, where is_file_name will be the name of the file you want to read through. Since you'll need some way of knowing when you need to do that, declare a user-object function called start() (declare it as public without return type and it will be a user-object subroutine). This function looks like Listing Three. Start() calls parse_file(), which opens the file and executes the ue_begin event. It then reads in a line and parses it into tokens, which are stored in an array. It then triggers the ue_scan event. It continues this until the end of the file, where it closes the file and triggers the ue_end event; see Listing Four.

The heart of this routine is the parse_line() function. You would normally consider this an internal (and therefore protected) function. However, since you'll want to parse things other than files, you make parse_line a public function. The function will return the number of fields; see Listing Five. The rest of the functions simply return the values stored in the protected variables; see Listing Six. As with any PowerBuilder nonvisual user object, you'll have to declare a variable of type uo_nv_awk (Listing Seven), then instantiate it. At this point, put the code in the events defined for the object; see Figure 3.

After that, to use the awk object, just give it a filename and call start(). Now you can do just about anything those UNIX guys can think of, plus a little moreUNIX awk can't link to a visual-programming tool such as PowerBuilder, and UNIX awk can't be inherited from and changed.

Figure 1: The User Object Painter.

Figure 2: Declaring user events.

Figure 3: Coding the user events.

For More Information

Powersoft

561 Virginia Road

Concord, MA 01742-2732

508-287-1500

http://www.powersoft.com

Listing One

Protected:
int ii_num_fields   // Equiv of $nf
long    il_num_lines        // Equiv of $nr
int ii_file_handle  // File Handle
string  is_current_line // Equiv of $0
string  isa_fields[]        // Fields

Public:
string  is_file_name
int ii_error_status

/* Error Return Code Constants */
int NO_ERROR        =  0
int NO_FILE     = -1
int FILE_NOT_EXIST  = -2
int CANNOT_OPEN_FILE    = -3
int FILE_READ_ERROR = -4
int INVALID_FIELD   = -5

Listing Two

public string field( int ai_field )
public int nf( )
public long nr( )
protected parse_file( )
public int parse_line( string as_line )
public start( )

Listing Three

/* Make sure they set the file name */
IF ( is_file_name = "" ) THEN
    ii_error_status = NO_FILE
    TriggerEvent( "ue_error" )
    return
END IF
/* Open the file, read only in line mode */
ii_file_handle = FileOpen(is_file_name, LineMode!, Read!)
IF ( ii_file_handle = -1 ) THEN
    ii_error_status = CANNOT_OPEN_FILE
    TriggerEvent( "ue_error" )
    return
END IF
parse_file( )

Listing Four

/* FileRead return types */
int     END_OF_FILE = -100
int READ_ERROR  = -1
int NO_CHAR     =  0
int li_num_char_read

/* We haven't read anything yet, so set the number of lines to 0 */

il_num_lines = 0

/* Start in the Begin event */
TriggerEvent( "ue_begin" )

/* Read through this until we hit the end of the file */
DO
    li_num_char_read = FileRead( ii_file_handle, is_current_line )
    CHOOSE CASE li_num_char_read
        CASE READ_ERROR
            ii_error_status = FILE_READ_ERROR
            TriggerEvent( "ue_error" )
        CASE END_OF_FILE
            // Nothing, the loop will terminate
        CASE ELSE
            parse_line( is_current_line )
            il_num_lines = il_num_lines + 1
            TriggerEvent( "ue_scan" )
    END CHOOSE
LOOP WHILE ( li_num_char_read <> END_OF_FILE )

FileClose( ii_file_handle )
TriggerEvent( "ue_end" )

Listing Five

int NO_MORE_DELIMITERS = 0  // We've hit the end of the line
long ll_pos         // position of the delimiter within the string

/* We are just starting, so set the number of fields to 0 */
ii_num_fields = 0

/* Get the string ready */
as_line = Trim( as_line )

/* Figure out where the first delimiter is */
ll_pos = Pos( as_line, " " )

/* Parse until position is 0 (which means 1 more record exists) */
DO WHILE ( ll_pos <> NO_MORE_DELIMITERS )
    /* We've found one, so increase our field counter */
    ii_num_fields = ii_num_fields + 1

    /* Store the field */
    isa_fields[ii_num_fields] = Trim( Left( as_line, ll_pos))

    /* Remove that part from our string */
    as_line = Trim( mid( as_line, ll_pos ))
    /* And find the next delimiter - if 0, allow one more go-round */
    ll_pos = Pos( as_line, " ")
LOOP
/* One more record may be left, and if so, store it */
IF ( as_line <> "" ) THEN
    ii_num_fields = ii_num_fields + 1
    isa_fields[ii_num_fields] = Trim( as_line )

END IF
return ii_num_fields

Listing Six

public string field( int ai_field ):
CHOOSE CASE ai_field
    CASE 0
        return is_current_line
    CASE 1 to nf( )
        return isa_fields [ ai_field ]
    CASE ELSE
        ii_error_status = INVALID_FIELD
        TriggerEvent( "ue_error" )
        return ""
END CHOOSE

public int nf( ):
return ii_num_fields

public long nr( ):
return il_num_lines

Listing Seven

int     i
uo_nv_awk   luo_awk
luo_awk = CREATE uo_nv_awk

luo_awk.parse_line( 'Hello World' )
FOR I = 1 TO luo_awk.nf( )
    messagebox( 'test', 'field '+string(i)+' is '+luo_awk.field( i ) )
NEXT

DESTROY iuo_nv_awk


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.