Project

General

Profile

Redirected Terminal Usage

The possibility to make the hardcopy report is the mandatory part of the every straightforward software project. The FWD implementation is not an exception. And here the redirection is taking the control to handle the process. By the word “hardcopy” we consider the report file generation. If follow in this chapter we use output to the physical printer this will be mentioned separately.

There are several 4GL statements directly related to the handling of the redirected terminal usage. The following table describes these statements. Note these are not the statements to make redirected output itself, only to control what is the current output will be:

4GL output statement Description Notes Supported
DEFINE NEW [GLOBAL SHARED] STREAM stream Establishing the stream for input or output. By default each process has one default unnamed input stream and one unnamed stream for output. The both of them are connected to the terminal. Yes
OUTPUT [STREAM stream] TO ... Opening the stream for output to the file, devise or terminal. All further output related statements will send the data to the redefined stream. Yes
INPUT [STREAM stream] FROM ... Opening the stream for getting data from file, device or terminal. All further input related statements will get the data from the redefined stream. Yes
OUTPUT [STREAM stream] CLOSE Closing the output stream saving buffered data. At the end the statement resets the output destination. Usually to the terminal. Yes
INPUT [STREAM stream] CLOSE Closing the input stream getting the buffered data. At the end the statement resets the input source. Usually to the terminal. Yes
EXPORT [STREAM stream] Creating data file to be read by another procedure. The output for is known and should be preserved while further importing. Data entries are separated by delimiter char. Yes
IMPORT Reading data from external file created by the other tools. The data reading format should be the same as used for previous exporting to get the consistent data. Yes
PUT Sending the data of the variable or expressions to the output other than terminal. Unlike the EXPORT statement this one can be used to sent the control characters. Also there is no space delimiter between data entries. Yes

The most of the procedures use the terminal as the input source when the user types any data into widget field and also as output source to display the changes or other valuable information. But this is not the only way the 4GL program can communicate with the world outside. The other possibilities are changing the output destination and the input source, defining additional input/output streams and sharing streams, reading the contents of a directory, exporting/importing data to other formats. Not all 4GL options are supported in Java converted code but the general concept is preserved.

When the application starts it has two unnamed streams by default - input and output. The both are connected to the terminal screen. When it is decided to use redirected input/output the stream must be defined, opened and after i/o operations are done - closed. This is common rule for every well-behaved application not for this case only but for applications from other programming languages that deals with the external files or devices. So this is important part of every business-related application. Let's consider the process in details step by step.

Defining streams

The input and output in 4GL and converted code is stream based. So before the alternative input/output can be used we have to define the new stream to use as the channel. The syntax of the statement is:

DEFINE [ [ NEW [ GLOBAL ] ] SHARED ] STREAM stream

We see different possible option for the statement. This produces a set of the available statement versions that can be used for a bit different purposes.

NEW SHARED STREAM stream defines the stream that can be shared by the other procedures. When the procedure defining this type of the stream ends the stream is no longer available for any procedure. Will be implemented in Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
SharedVariableManager.addStream("mystrStream", mystrStream);
TransactionManager.registerFinalizable(mystrStream, false);

NEW GLOBAL SHARED STREAM stream defines the stream that can be shared by the other procedure and that remains available even after the procedure creates this type of the stream ends. Will be implemented in Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
SharedVariableManager.addStream(ScopeLevel.GLOBAL, "mystrStream", mystrStream);
TransactionManager.registerFinalizable(mystrStream, true);

SHARED STREAM stream defines the stream that was created by the another procedure using either DEFINE NEW SHARED STREAM or DEFINE NEW GLOBAL SHARED STREAM statements. This means we request already existed stream for using. Will be implemented in Java as:

Stream mystrStream = SharedVariableManager.lookupStream("mystrStream");

STREAM stream defines the stream that can only be used by the procedure containing the DEFINE STREAM statement. Will be implemented in Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
TransactionManager.registerTopLevelFinalizable(mystrStream, true);

Note the difference between these calls. The starting point is the stream creation. The class StreamWrapper is used for this purpose. Then depending on will the stream be shared or not the handling is different. Shared streams are controlled by the SharedVariableManager class. The call SHARED STREAM does not create new stream. It finds already existed one if possible. And finally the simplest version of the stream creation is having the local stream usable inside the procedure that creates it. For this case there is only the finalizable registration call that ensures the execution of some last calls upon stream closing. This finalization is performed in every case the stream is really created. The exception is SHARED STREAM statement when the stream handling code is located inside the procedure from which the stream is coming in.

The simple real example can be the following 4GL code:

define stream mystr.
output stream mystr to "redirected_training4.txt".
display stream mystr "Hello Outside World!".
output stream mystr close.

will be implemented in Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
frame0.openScope();
TransactionManager.registerTopLevelFinalizable(mystrStream, true);
mystrStream.assign(StreamFactory.openFileStream("redirected_training4.txt", true, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Hello Outside World!", frame0.widgetExpr1())
};

frame0.display(mystrStream, elementList0);

mystrStream.closeOut();
...

this is the complete cycle of the alternative stream application. The contents of the file "redirected_training4.txt" is:

Hello Outside World!

The other more complex example is for using the shared streams. In the one of the following 4GL file we define the stream and run the procedure that will use it:

define new shared stream mystr6.
output stream mystr6 to "redirected_training6.txt".
run "./redirected_training60.p".
output stream mystr6 close.

The procedure code is:

define shared stream mystr6.
display stream mystr6 "Hello Outside World!".

This will be converted in Java as:

Stream mystr6Stream = new StreamWrapper("mystr6");
...
SharedVariableManager.addStream("mystr6Stream", mystr6Stream);
TransactionManager.registerFinalizable(mystr6Stream, false);
mystr6Stream.assign(StreamFactory.openFileStream("redirected_training6.txt", true, false));
RedirectedTraining60 redirectedtraining600 = new RedirectedTraining60();
redirectedtraining600.execute();
mystr6Stream.closeOut();

And the procedure is:

Stream mystr6Stream = SharedVariableManager.lookupStream("mystr6Stream");
...
FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Hello Outside World!", frame0.widgetExpr1())
};

frame0.display(mystr6Stream, elementList0);

The resulting output file is the same as in previous example. The next example illustrates the global shared stream using. The 4GL part consist of the three source files. The first one is the main procedure runner:

run "./redirected_training241.p".
run "./redirected_training240.p".

will be implemented in Java as:

RedirectedTraining241 redirectedtraining2410 = new RedirectedTraining241();
redirectedtraining2410.execute();
RedirectedTraining240 redirectedtraining2401 = new RedirectedTraining240();
redirectedtraining2401.execute();

this is the simple procedures executions. Note the second procedure will be run after the first one has been completed. The first procedure is:

define new global shared stream mystr6.
output stream mystr6 to "redirected_training24.txt".
display stream mystr6 "Hello Outside World!".

Will be implemented in Java as:

...
Stream mystr6Stream = new StreamWrapper("mystr6");
...
         frame0.openScope();
         SharedVariableManager.addStream(ScopeLevel.GLOBAL, "mystr6Stream", mystr6Stream);
         TransactionManager.registerFinalizable(mystr6Stream, true);
         mystr6Stream.assign(StreamFactory.openFileStream("redirected_training24.txt",
                                                          true, false));

         FrameElement[] elementList0 = new FrameElement[]
         {
            new Element("Hello Outside World!", frame0.widgetExpr1())
         };

         frame0.display(mystr6Stream, elementList0);
...

Note this procedure defines GLOBAL SHARED STREAM. Then it makes the output and finishes the work. The second procedure is:

define shared stream mystr6.
display stream mystr6 skip(1).
display stream mystr6 "Global Stream is still alive!".

Will be implemented in Java as:

...
Stream mystr6Stream = SharedVariableManager.lookupStream("mystr6Stream");
...
         frame0.openScope();

         frame0.display(mystr6Stream);

         FrameElement[] elementList1 = new FrameElement[]
         {
            new Element("Global Stream is still alive!", frame0.widgetExpr2())
         };

         frame0.display(mystr6Stream, elementList1);
...

It just looks up the opened stream and append some new data into it. The output file contains the following two lines:

Hello Outside World!
Global Stream is still alive!

This means the second procedure did find the stream to make output even after the first procedure has been completed. This is the main feature of the shared stream if it defines with GLOBAL modifier. Note the first procedure should not close the stream explicitly and does no close the stream Implicitly. This closing will produce IO error in the attempting to write into closed stream.

Several considerations to take into account while screen definition.

It is not possible to define a SHARED or NEW SHARED stream in a user-defined function, an internal procedure or a persistent procedure.

The system automatically provides two unnamed streams to each procedure: the input stream and output stream. This gives the possibility to bypass the stream definition statements to get data from the file and provide output to the file. The syntax of this call is:

INPUT FROM test_redirected.txt

will be implemented in Java as:

UnnamedStreams.assignIn(StreamFactory.openFileStream("test_redirected.txt", false, false));

All further input statements will get the data from file instead of usual terminal.

Using the DEFINE STREAM statement creates a stream but it does not actually open that stream. Before stream usage the statements INPUT FROM or OUTPUT TO should be used or the stream must be specified as one of the parameter inside the data input/output statements.

Opening the redirected streams

Once the stream has been defined it must be opened before doing any operations for input/output. There are two possible ways to do this. It is possible to use the OUTPUT TO/INPUT FROM statement pair to redefine the default i/o operations to the external storage for any following input and output statements for example, or specify the redirected stream as the parameter for i/o calls.

OUTPUT TO Statement

Use this statement to redirect output form the default terminal unnamed stream to the external device. The 4GL syntax of the statement is:

OUTPUT [ STREAM stream ] TO
   { PRINTER [ printer-name ]
      | opsys-file
      | opsys-device
      | TERMINAL
      | VALUE ( expression )
      | "CLIPBOARD" 
   }
   [ NUM-COPIES { constant | VALUE ( expression ) } ]
   [ COLLATE ]
   [ LANDSCAPE | PORTRAIT ]
   [ APPEND ]
   [ BINARY ]
   [ ECHO | NO-ECHO ]
   [ KEEP-MESSAGES ]
   [ NO-MAP | MAP protermcap-entry ]
   [ PAGED ]
   [ PAGE-SIZE { constant | VALUE ( expression ) } ]
   [ UNBUFFERED ]
   [     NO-CONVERT
      |  { CONVERT
            [ TARGET target-codepage ]
            [ SOURCE source-codepage ]
         }
   ]

The option description table:

4GL syntax Description Supported
STREAM stream Specifies the name of the stream. In case of not specified name unnamed stream is used. Yes
PRINTER [ printer-name ] By default, this option sends output to the printer defined in the default print context. The printer-name parameter can be used to send output to a specific printer. This option overrides but does not change the default print context. The output is considered to be paged unless PAGE-SIZE 0 option has been specified. No
opsys-file The name of the text file to which it is required to direct output from a procedure. Yes
opsys-device Represents the name of the operating system device  
TERMINAL Directs the output to the terminal which is the default output device Yes
VALUE ( expression ) Represents an expression to be used as the destination of the output. Yes
“CLIPBOARD” The system clipboard as destination. No
NUM-COPIES { constant | VALUE (expression) } Specifies the number of copies to print. The constant or expression parameters must evaluate to a positive integer. No
COLLATE Specifies whether multiple copies of output pages print in collated order. No
LANDSCAPE | PORTRAIT Page orientation for Windows only for drivers that support such orientations. No
APPEND Appends the output to the end of a file Yes
BINARY Allows output to be written directly without any conversion or interpretation. No
ECHO Sends all input data read from a file to the output destination. Data is echoed by default. No
NO-ECHO Suppress the echoing of input data to the output destination. No
KEEP-MESSAGES Causes the following messages not to echo to the default window: 4GL error and warning messages, and messages from the MESSAGE statement. This option causes sending messages only to the specified output stream. No
MAP protermcap-entry | NO-MAP Using this option to send output to a device that requires different character mapping comparing to mapping applied to the current output stream. No
PAGED Formats the output into pages. Form feed chars are CTRL-L. When output is paged, a page break occurs every 56 lines. PAGED is automatic for output to a printer. Yes
PAGE-SIZE { constant | VALUE ( expression ) } Specifies the number of lines per page. The expression is a constant, field name, variable name, or expression with the integer value. Yes
UNBUFFERED Disables usual OS buffering tool to speed up the i/o operations. No
CONVERT Allows to modify the character conversion occurring the external file and memory. No
TARGET target-codepage Specifies the target code page of the character conversion. The name specified must be the valid code page. No
SOURCE source-codepage Specifies the source code page of the character conversion. The name specified must be the valid code page. No
NO-CONVERT Disables the character conversion between external file and memory. No

The Java conversion counterpart is:

assign(Stream stream)

of the StreamWrapper class. The Stream stream parameter is provided by one of the following static StreamFactory calls:

Stream openFileStream(java.lang.String filename, boolean write, boolean append,
                      int pageSz, boolean paged)
Stream openFileStream(java.lang.String filename, boolean write, boolean append, int pageSz)
Stream openFileStream(java.lang.String filename, boolean write, boolean append)
Stream openFileStream(java.lang.String filename, boolean write, boolean append, boolean paged)

All these calls construct the remote stream representing a file using filename, this file or device will be used in read or write operations based on the given write flag. The current read/write position is 0 unless the append option is true. The appending means the positioning to the end of file.

java.lang.String filename - the name of the file or device to open,

boolean write - if thue, open file in writing mode, otherwise - reading,

boolean append - in writing mode this positions file pointer to the end of the file causing new data to be appended to the already existed. Ignored in read mode,

int pageSz - the size of the page. -1 means default paging,

boolean paged - true means that stream is paged.

Consider the 4GL options on particular examples. The only mandatory option to be presented in every call of the OUTPUT TO statement is the name of the external device, file, printer, terminal and so on.

The following example demonstrates the usage of the unnamed stream to redirect output to the external file. 4GL code:

output to "redirected_training7.txt".
display "Hello Outside World! Unnamed stream usage".

will be implemented in Java as:

UnnamedStreams.assignOut(StreamFactory.openFileStream("redirected_training7.txt", true, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Hello Outside World! Unnamed stream usage", frame0.widgetExpr1())
};

frame0.display(elementList0);

The contents of the output file is:

Hello Outside World! Unnamed stream usage

The next example uses defined stream to redirect output to the file. The 4GL code is:

define stream mystr.
output stream mystr to "redirected_training8.txt".
display stream mystr "Hello Outside World!".
output stream mystr close.

will be implemented in Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
TransactionManager.registerTopLevelFinalizable(mystrStream, true);
mystrStream.assign(StreamFactory.openFileStream("redirected_training8.txt", true, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Hello Outside World!", frame0.widgetExpr1())
};

frame0.display(mystrStream, elementList0);

mystrStream.closeOut();

Note the DISPLAY statement here. The parameter Stream must be used with this statement to file output. In the following example we consider the usage of the TERMINAL option of the OUTPUT TO statement. This is the way the output channel can be restored to the default one. The 4GL code is:

output to "redirected_training9.txt".
display "Hello Outside World!".
output to terminal.
pause.
display "This should be on the screen!".
pause.

will be implemented in Java as:

UnnamedStreams.assignOut(StreamFactory.openFileStream("redirected_training9.txt", true, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Hello Outside World!", frame0.widgetExpr1())
};

frame0.display(elementList0);

UnnamedStreams.assignOut(null);
pause();

FrameElement[] elementList1 = new FrameElement[]
{
   new Element("This should be on the screen!", frame0.widgetExpr2())
};

frame0.display(elementList1);
pause();

The application writes data to the file but after the pause we will see the screen:

If not to use the TERMINAL option the output will still go to the file.

Sometimes it is not suitable to have the hardcoded external devices in the application. To be able to have the opportunity to program the output channel name on the fly the option VALUE can be used. In the following example the out put will be redirected the device whose name is specified in the expression variable. The 4GL code is:

def var chout as character format "x(40)" initial "redirected_training10.txt".
message "The output will be redirected to "+chout.
pause.
output to value( chout ).
display "Hello Outside World!".

will be implemented in Java as:

character chout = new character("redirected_training10.txt");
...
message(concat(new character("The output will be redirected to "), chout));
pause();
UnnamedStreams.assignOut(StreamFactory.openFileStream((chout).toStringMessage(), true, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Hello Outside World!", frame0.widgetExpr1())
};

frame0.display(elementList0);

So the output file name in this example will be defined by the value of the chout character variable.

The option APPEND can be used to concatenate the new data to the already existed. The following 4GL example demonstrates this option:

define stream mystr.
output stream mystr to "redirected_training11.txt" append.
display stream mystr "Redirectwd output in appended mode".
output stream mystr close.

will be implemented in Java as:

...
TransactionManager.registerTopLevelFinalizable(mystrStream, true);
mystrStream.assign(StreamFactory.openFileStream("redirected_training11.txt", true, true));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element("Redirectwd output in appended mode", frame0.widgetExpr1())
};

frame0.display(mystrStream, elementList0);
...

The first run of the application creates file if not exist and writes one line of the text. The second run opens existed file and adds the next line and so on. The fact of initializing the APPEND mode can be seen from the true value of the boolean append parameter of the StreamFactory.openFileStream() call.

The output files can be paged. Use the PAGED option of the OUTPUT TO statement. Consider the following 4GL code:

output to "redirected_training12.txt" paged.
repeat i = 1 to 100:
   down 0 with frame f1.
   display "Paged output, line" line-counter with frame f1.
end.

will be implemented in Java as:

...
UnnamedStreams.assignOut(
   StreamFactory.openFileStream("redirected_training12.txt", true, false, true));
...
ToClause toClause0 = new ToClause(i, 1, 100);

repeatTo("loopLabel0", toClause0, new Block()
{
   f1Frame.down(0);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element("Paged output, line", f1Frame.widgetExpr1()),
      new Element(UnnamedStreams.safeOutput().getNextLineNum(), f1Frame.widgetExpr2())
   };

   f1Frame.display(elementList0);
...
});

The output will be paged every 56 lines by inserting the page separator. This is the default pagination value. If it is required to have another value for the size of the report page the option PAGE-SIZE should be used. The 4GL code for this case is:

output to "redirected_training13.txt" paged page-size 10.
repeat i = 1 to 30:
   down 0 with frame f1.
   display "Paged output, line" line-counter with frame f1.
end.

will be implemented in Java as:

UnnamedStreams.assignOut(
   StreamFactory.openFileStream("redirected_training13.txt", true, false, 10, true));
...
ToClause toClause0 = new ToClause(i, 1, 30);

repeatTo("loopLabel0", toClause0, new Block()
{
...
   f1Frame.down(0);

   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element("Paged output, line", f1Frame.widgetExpr1()),
      new Element(UnnamedStreams.safeOutput().getNextLineNum(), f1Frame.widgetExpr2())
   };

   f1Frame.display(elementList0);
...
});

This code will insert the page separator every 10 lines. The only implementation difference in another version of the StreamFactory.openFileStream() call is used. The value 0 specified as page size disables the pagination completely. No page separator will be inserted in this case.

General consideration for using OUTPUT TO statement. OUTPUT TO TERMINAL statement is the default unless the procedure was called by another procedure while a different output destination was active. The output destination at the beginning of the procedure is the current output destination of the calling procedure. The OUTPUT TO TERMINAL PAGED statement clears the screen and displays output on scrolling pages with the length of the screen. The system pauses before each page header and this can be adjusted using the PAUSE statement. If the terminal report is wider than the screen, the report will be wrapped. It is planned to use the output file as the input one for another procedure consider to use the EXPORT statement instead. If the output is sending to target other than terminal the ROW option of the frame will be ignored. Also the frame boxes will not be displayed, the top line will be converted to the blank spaces. All messages will be sent to the current output destination. The memory-output character conversion is defined by the current language settings and packages installed in JRE which is used to run the converted Java application.

INPUTUT FROM Statement

Use this statement to redirect input form the default terminal unnamed stream to the external device. The 4GL syntax of the statement is:

The 4GL syntax of the statement is:

INPUT [ STREAM stream ] FROM
   {    opsys-file
      | opsys-device
      | TERMINAL
      | VALUE ( expression )
      | OS-DIR ( directory ) [ NO-ATTR-LIST ]
   }
   [ BINARY ]
   [ ECHO | NO-ECHO ]
   [ KEEP-MESSAGES ]
   [ NO-MAP | MAP protermcap-entry ]
   [ UNBUFFERED ]
   [     NO-CONVERT
      |  { CONVERT
            [ TARGET target-codepage ]
            [ SOURCE source-codepage ]
         }
   ]

The option description table:

4GL syntax Description Supported
STREAM stream Specifies the name of the stream. In case of not specified name unnamed stream is used. Yes
opsys-file The name of the text file to which it is required to direct output from a procedure. Yes
opsys-device Represents the name of the operating system device  
TERMINAL Directs the output to the terminal which is the default output device Yes
VALUE ( expression ) Represents an expression to be used as the destination of the output. Yes
OS-DIR ( directory ) [ NO-ATTR-LIST ]   No
BINARY Allows output to be written directly without any conversion or interpretation. No
ECHO Sends all input data read from a file to the output destination. Data is echoed by default. No
NO-ECHO Suppress the echoing of input data to the output destination. No
KEEP-MESSAGES Causes the following messages not to echo to the default window: 4GL error and warning messages, and messages from the MESSAGE statement. This option causes sending messages only to the specified output stream. No
MAP protermcap-entry | NO-MAP Using this option to send output to a device that requires different character mapping comparing to mapping applied to the current output stream. No
UNBUFFERED Disables usual OS buffering tool to speed up the i/o operations. No
CONVERT Allows to modify the character conversion occurring the external file and memory. No
TARGET target-codepage Specifies the target code page of the character conversion. The name specified must be the valid code page. No
SOURCE source-codepage Specifies the source code page of the character conversion. The name specified must be the valid code page. No
NO-CONVERT Disables the character conversion between external file and memory. No

The Java counterpart is:

assignIn(Stream stream)

for the unnamed streams and

assign(Stream stream)

for the named ones

Consider the 4GL options on particular examples. The only mandatory option to be presented in every call of the INPUT FROM statement is the name of the external device, file, terminal and so on.

The following example demonstrates the usage of the unnamed stream to redirect input from the external file. 4GL code:

def var ch1 as character.
def var ch2 as character.
def var ch3 as character.

input from "redirected_training15.txt".
update ch1 ch2 ch3.
display ch1 ch2 ch3.

will be implemented in Java as:

UnnamedStreams.assignIn(StreamFactory.openFileStream("redirected_training15.txt", false, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1=()),
   new Element(ch2, frame0.widgetCh2()),
   new Element(ch3, frame0.widgetCh3())
};

frame0.update(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1()),
   new Element(ch2, frame0.widgetCh2()),
   new Element(ch3, frame0.widgetCh3())
};

frame0.display(elementList1);

The initial file contains:

Hello Outside World!

The screen after application starts will be:

Note to read the whole line we need to have 3 character variables.

The next example uses defined stream to redirect input from the file. The 4GL code is:

def var ch1 as character.
def var ch2 as character.
def var ch3 as character.
define stream mystr.

input stream mystr from "redirected_training15.txt".
update stream mystr ch1 ch2 ch3.
display ch1 ch2 ch3.
input stream mystr close.

will be implemented in Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
TransactionManager.registerTopLevelFinalizable(mystrStream, true);
mystrStream.assign(StreamFactory.openFileStream("redirected_training15.txt", false, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1()),
   new Element(ch2, frame0.widgetCh2()),
   new Element(ch3, frame0.widgetCh3())
};

frame0.update(mystrStream, elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1()),
   new Element(ch2, frame0.widgetCh2()),
   new Element(ch3, frame0.widgetCh3())
};

frame0.display(elementList1);

mystrStream.closeIn();

The output result will be the same as for the previous example. Note the update call has the parameter Stream mystrStream. This is the way we inform the system we need the input channel to be associated with the particular stream. And this stream has been bound to the external file. To restore the terminal input the INPUT FROM statement must be called with the TERMINAL option like in the example below. The 4GL code:

...
input from "redirected_training15.txt".
update ch1.
display ch1.
message "This is the input from file, go back to terminal".
pause.
input from terminal.
update ch2.
pause.

will be implemented in Java as:

UnnamedStreams.assignIn(StreamFactory.openFileStream("redirected_training15.txt", false, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1())
};

frame0.update(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1())
};

frame0.display(elementList1);

message("This is the input from file, go back to terminal");
pause();
UnnamedStreams.assignIn(null);

FrameElement[] elementList2 = new FrameElement[]
{
   new Element(ch2, frame0.widgetCh2())
};

frame0.update(elementList2);
pause();

The first screen after the application starts is:

Note the first UPDATE statement takes the data from the external file. Press spacebar to continue. The screen become:

Here we see the variable ch2 is updated via terminal. This means we switched the input stream back to the terminal input.

Sometimes it is useful not to have the hardcoded input name file stream. For example in a cases the input file names will be changed in every new session. The option VALUE must be used for this purpose. The following 4GL code:

def var ch1 as character.
def var ch2 as character.
def var ch3 as character.

def var chout as character format "x(40)" initial "redirected_training18.txt".
message "The input will be redirected from "+chout.
pause.

input from value( chout ).
update ch1 ch2 ch3.
display ch1 ch2 ch3.

will be converted in Java as:

message(concat(new character("The input will be redirected from "), chout));
pause();
UnnamedStreams.assignIn(StreamFactory.openFileStream((chout).toStringMessage(), false, false));

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1()),
   new Element(ch2, frame0.widgetCh2()),
   new Element(ch3, frame0.widgetCh3())
};

frame0.update(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(ch1, frame0.widgetCh1()),
   new Element(ch2, frame0.widgetCh2()),
   new Element(ch3, frame0.widgetCh3())
};

frame0.display(elementList1);

The initial screen will be:

Press spacebar to start the data reading from file:

So the input file name in this example will be defined by the value of the chout character variable.

General consideration for using INPUT FROM statement. To close the current input to a procedure, use the INPUT CLOSE statement( the input source is automatically closed at the end of the procedure or when another default input source is opened). If the statements PROMPT-FOR, SET or UPDATE is used to read the data from a file, the FORMAT for the data is ignored. Therefore, if the application is dependent on the FORMAT option to validate the input the invalid character can be read. If end of the file is reached, the system generates the ENDKEY event. The single period encountered in the input file generates the END-ERROR event to happen. To read the period as character it must be enclosed in quotes(“.”). There are the special characters to use in a input files. The first one group is tilde(~*) or slash(\*) on UNIX. This symbol encountered in the end of the line means the next line will be considered as concatenation of the previous one. There must be no space char after tilde before the next line. For example:

Hello~
Outside

will be read as single line int char variable with value HelloOutside. The second special character is hyphen. The using of the hyphen(-) causes the variable setting statement does not change the appropriate variable. The input-memory conversion is based on the currently installed Java LANG system variable and locale packages installed in JRE under which the converted application is executing.

Special purpose statements

When working with input/output redirecting and data exchange via external file it may require to organize the output data structure specifically. There is the set of the statements that can be useful in these attempts. The statements related to data redirection in this case are: EXPORT, IMPORT and PUT. EXPORT statement converts data to a standard character format and displays it to the current output destination or to a named output stream. IMPORT statement reads the line from an input file that might be created by the preceding EXPORT statement or other external tool according to the EXPORT statement style. PUT statement prepares the data in a fixed format possibly more acceptable to use by another system. In the following sections we consider these statements in details on particular examples applying to the subject of this chapter.

EXPORT statement

The statement is intended to provide the data file compatible with following IMPORT statement to use with another FWD procedure. The known format is the set of lines consisted of the character data separated by the delimiter char. By default the delimiter is one space character.

The 4GL syntax of the statement is:

EXPORT [ STREAM stream ] [ DELIMITER character ]
   {   expression …
     | record [ EXCEPT field ... ]
   }

The option description table:

4GL syntax Description Supported
STREAM stream Specifies the name of the stream. In case of not specified name unnamed stream is used. Yes
DELIMITER character The character to use as a delimiter between field values. The character must be a quoted single character. The default value is a space character. Yes
expression One or more expression to convert into standard character format to an output destination.  
record The name of the record buffer with fields that should be converted for outputting to the file. Yes
[ EXCEPT field ] The name of the field that must be excluded from representation in output file when using the record option mentioned above. Yes

The Java counterpart for this statement is:

Stream.export( FieldEntry data[] )

data is the array list of the fields to be exported. The DELIMITER option is the feature of the Stream class and can be set before the export call.

The parameter STREAM stream is optional and can be omitted. In this case the output should be redirected to the file by the OUTPUT TO statement before using the EXPORT statement. Consider the following simple example:

def var chVar1 as char initial "Hello".
def var chVar2 as char initial "Export".
def var intVar as integer initial 20.
def var decVar as decimal initial 3.14.

output to "redirected_training25.txt".
export chVar1 chvar2 intVar decVar.

Will be converted to Java as:

character chVar1 = new character("Hello");
character chVar2 = new character("Export");
integer intVar = new integer(20);
decimal decVar = new decimal(3.14);
...
public void body()
{
   UnnamedStreams.assignOut(StreamFactory.openFileStream("redirected_training25.txt", true, false));
   UnnamedStreams.safeOutput().export(new FieldEntry[]
   {
      new ExportField(chVar1),
      new ExportField(chVar2),
      new ExportField(intVar),
      new ExportField(decVar)
   });

After application completed the output file is:

"Hello" "Export" 20 3.14

Note the data formatting in the output line. Data are converted to the character format with adding ” chars to the character variables. The unknown values will be exported as unquoted question mark (?). The logical fields will be exported as YES or NO. The delimiter is the default space character for this example. The next example is used the defined stream as transport for the EXPORT statement. The 4GL code is:

define stream mystr.
output stream mystr to "redirected_training26.txt".
export stream mystr chVar1 chvar2 intVar decVar.

Converting to Java as:

Stream mystrStream = new StreamWrapper("mystr");
...
      public void body()
      {
         TransactionManager.registerTopLevelFinalizable(mystrStream, true);
         mystrStream.assign(StreamFactory.openFileStream("redirected_training26.txt", true, false));
         mystrStream.export(new FieldEntry[]
         {
            new ExportField(chVar1),
            new ExportField(chVar2),
            new ExportField(intVar),
            new ExportField(decVar)
         });

After application finished the output file will be the same as in previous example. Note now the export call is associated with the defined class instance mystrStream instead of the static class UnnamedStreams usage.

Now let's change the delimiter style. The 4GL code is:

export delimiter ";" chVar1 chvar2 intVar decVar.

Converting to Java as:

UnnamedStreams.safeOutput().setDelimiter(";");
UnnamedStreams.safeOutput().export(new FieldEntry[]
{
   new ExportField(chVar1),
   new ExportField(chVar2),
   new ExportField(intVar),
   new ExportField(decVar)
});

When application completes the output file is:

"Hello";"Export";20;3.14

Note the default delimiter style has been changed. The delimiter used in EXPORT statement must be used in corresponding IMPORT statement to read this line. The next example illustrates the usage of the EXPORT statement with the record option. It allows to perform the output of the whole database record in a short command. The 4GL sample:

export book.

Will be converted to:

UnnamedStreams.safeOutput().export(new FieldEntry[]
{
   new ExportField(new FieldReference(book, "bookId")),
   new ExportField(new FieldReference(book, "bookTitle")),
   new ExportField(new FieldReference(book, "publisher")),
   new ExportField(new FieldReference(book, "isbn")),
   new ExportField(new FieldReference(book, "onHandQty")),
   new ExportField(new FieldReference(book, "cost")),
   new ExportField(new FieldReference(book, "pubDate")),
   new ExportField(new FieldReference(book, "authorId")),
   new ExportField(new FieldReference(book, "soldQty")),
   new ExportField(new FieldReference(book, "price"))
});

Note the record internals is converted to the array of the simple data fields. If we do not need all record to be exported we have to provide the exceptions explicitly by the EXCEPT option. Let's exclude two fields: price and isbn. The 4GL statement for this case is:

export book except book.price book.isbn.

converting to Java as:

UnnamedStreams.safeOutput().export(new FieldEntry[]
{
   new ExportField(new FieldReference(book, "bookId")),
   new ExportField(new FieldReference(book, "bookTitle")),
   new ExportField(new FieldReference(book, "publisher")),
   new ExportField(new FieldReference(book, "onHandQty")),
   new ExportField(new FieldReference(book, "cost")),
   new ExportField(new FieldReference(book, "pubDate")),
   new ExportField(new FieldReference(book, "authorId")),
   new ExportField(new FieldReference(book, "soldQty"))
});

Note the changing of the Java converted code.

IMPORT statement

The statement is intended to read the line of data created previously by the EXPORT statement or any other tool creating compatible data line format. The known format is the set of lines consisted of the character data separated by the delimiter char. By default the delimiter is one space character.

The 4GL syntax of the statement is:

IMPORT [ STREAM stream ]
   {   [ DELIMITER character ] { field | ^ } ...
     | [ DELIMITER character ] record [ EXCEPT field ... ]
     | UNFORMATTED field
   }
   [ NO-ERROR ]

The option description table:

4GL syntax Description Supported
STREAM stream Specifies the name of the stream. In case of not specified name unnamed stream is used. Yes
DELIMITER character The character to use as a delimiter between field values. The character must be a quoted single character. The default value is a space character. Yes
field One or more expression to convert from the standard character format and to place the data into. Yes
^ Special symbol indicating skip over the next line. Yes
record The name of the record buffer to import data from the file. Yes
[ EXCEPT field ] The name of the field to be excluded from importing process using the external data file when using the record option mentioned above. Yes
UNFORMATTED Turn on the read whole line mode instead of the single field mode that is used without this option. The line of data has been read with single call moving the file pointer to the next line. Yes
NO-ERROR Suppressing the system error handling allowing to check the error status and decide what to do next. Yes

The Java counterpart for this statement is:

Stream.readField( BaseDataType var )

or

Stream.readLine( BaseDataType var )

the difference is the amount of the data to be read. The readLine() is used to get the whole line in one call while the readField() method is reading the next single data entry between two delimiter characters. In the case of the single field reading if we do not reach the end of the line and want to move to the next line the Stream.resetCurrentLine() should be called explicitly to move to the next line. This is performed automatically only if we just read the last data entry in the current line.

As for the previously considered EXPORT statement the Stream stream parameter is optional and can be omitted. In this case we have to redirect input before using the IMPORT FROM statement. Consider the following example.

def var chVar1 as char.
def var chVar2 as char.
def var intVar as integer.
def var decVar as decimal.

input from "redirected_training29.txt".
import chVar1 chvar2 intVar decVar.
input close.
display chVar1 chvar2 intVar decVar.

Will be converted to Java as:

character chVar1 = new character("");
character chVar2 = new character("");
integer intVar = new integer(0);
decimal decVar = new decimal(0);
...
   UnnamedStreams.assignIn(StreamFactory.openFileStream("redirected_training29.txt", false, false));
   UnnamedStreams.safeInput().readField(chVar1);
   UnnamedStreams.safeInput().readField(chVar2);
   UnnamedStreams.safeInput().readField(intVar);
   UnnamedStreams.safeInput().readField(decVar);
   UnnamedStreams.safeInput().resetCurrentLine();
   UnnamedStreams.closeIn();

   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(chVar1, frame0.widgetChVar1()),
      new Element(chVar2, frame0.widgetChVar2()),
      new Element(intVar, frame0.widgetIntVar()),
      new Element(decVar, frame0.widgetDecVar())
   };
   frame0.display(elementList0);

The initial input file contains the following:

"Hello" "Import" 44 6.28

After application starts the screen is:

Note the implementation of the IMPORT statement here. First the static call of the UnnamedStreams class is used here. And second - we are sequentially getting the next field of the line from first to last via readField() method. And third - we move the line counter explicitly to the next line by resetCurrentLine().

The next example used the name stream to import the text. The 4GL code is:

def var chVar1 as char format "x(40)".

define stream mystr.
input stream mystr from "redirected_training30.txt".
import stream mystr chVar1.
input stream mystr close.
display chVar1.

Converting to Java as:

TransactionManager.registerTopLevelFinalizable(mystrStream, true);
mystrStream.assign(StreamFactory.openFileStream("redirected_training30.txt", false, false));
mystrStream.readField(chVar1);
mystrStream.resetCurrentLine();
mystrStream.closeIn();

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(chVar1, frame0.widgetChVar1())
};

frame0.display(elementList0);

the input file contains the following data:

"Hello Import long string variable" 44 6.28

After application starts the screen is:

Note the implementation of the IMPORT statement is a bit different here. We use particular Stream class instance.

The next example demonstrates the changing of the DELIMITER character value. Consider the following 4GL code:

import delimiter ";" intVar1 intvar2.

Will be converted to Java as:

UnnamedStreams.safeInput().setDelimiter(";");
UnnamedStreams.safeInput().readField(intVar1);
UnnamedStreams.safeInput().readField(intVar2);
UnnamedStreams.safeInput().resetCurrentLine();

the initial file to read is:

1234,5678

As you can see we have changed the default delimiter style for input file. After application starts the screen:

Note we are redefined the delimiter for both input file and import code and this causes the delimiter character to be not the same. As the result we do not read the two integer values. These values were considered as the single integer. Changing the code to:

import delimiter "," intVar1 intvar2.

converted as

UnnamedStreams.safeInput().setDelimiter(",");
UnnamedStreams.safeInput().readField(intVar1);
UnnamedStreams.safeInput().readField(intVar2);
UnnamedStreams.safeInput().resetCurrentLine();

will different screen result after starting:

Note now we can separate two values synchronizing delimiter character used for import with one was used when the data were exported to the file. The next example demonstrates the usage of the record option with the IMPORT statement. This is used to fill the database entries from the external file. The 4GL code will be:

import book.

Converting to Java as:

UnnamedStreams.safeInput().readField(new FieldReference(book, "bookId"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "bookTitle"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "publisher"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "isbn"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "onHandQty"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "cost"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "pubDate"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "authorId"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "soldQty"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "price"));
UnnamedStreams.safeInput().resetCurrentLine();

Note the all record fields are explicitly unwrapped from the book record and used in readField() call as if it was the standalone fields or variables. The next example will demonstrate the EXCEPT option that excludes specified field from the importing process. The 4GL code for this case is:

import book except cost publisher.

converting to Java as:

UnnamedStreams.safeInput().readField(new FieldReference(book, "bookId"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "bookTitle"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "isbn"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "onHandQty"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "pubDate"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "authorId"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "soldQty"));
UnnamedStreams.safeInput().readField(new FieldReference(book, "price"));
UnnamedStreams.safeInput().resetCurrentLine();

Note the fields cost and publisher have been excluded from the list of the field to read into. The next important option we need to consider is the UNFORMATTED one. This tells the system to consider the data line inside the file as single character variable and read it as the single line call. The 4GL code for this case is:

import unformatted chVar1.
display chVar1.

Converting to Java as:

UnnamedStreams.safeInput().readLine(chVar1);

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(chVar1, frame0.widgetChVar1())
};

frame0.display(elementList0);

The input file to read will be:

1234 5678 This Is The String

After application starts the screen is:

Note the variable takes not only the first delimited value but the whole line instead. Also the implementation of the IMPORT statement is a bit different. Here we have readLine() call. And the resetCurrentLine() is not called explicitly. The line counter incrementing is embedded inside the readLine() call.

The next option to consider is the possibility to skip over the next field in the line or even the whole line. The appropriate option is the ^ character inside the IMPORT statement. The following 4GL code:

import ^ chVar1.
display chVar1.

Will be converted to Java as:

UnnamedStreams.safeInput().skipField();
UnnamedStreams.safeInput().readField(chVar1);
UnnamedStreams.safeInput().resetCurrentLine();

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(chVar1, frame0.widgetChVar1())
};

frame0.display(elementList0);

Note the ^ option has been converted to the Stream.skipField() call. This moves the internal file pointer to the next data field within the current line. If the input file has the following content:

1234 5678 This Is The String

After application starts the screen is:

Note the variable takes the second field from line. Moreover we can skip over the whole line if the IMPORT statement has the following syntax:

import ^.

This can be used to selective data reading. The next important option to consider is the NO-ERROR one. Use this option if it is required some special error processing. When the system encounters the issue it displays the error message and interrupt the current procedure or transaction block. And this can potentially destroy the normal execution flow especially if we heavily use the interactive user interface. To be able to take control over the error processing the option NO-ERROR can be added to the IMPORT statement for the cases when there are the some import issues. For example consider the following 4GL code:

def var intVar1 as integer.

input from "redirected_training37.txt".
import intVar1 no-error.
display intVar1.
message error-status:get-message(error-status:num-messages).

Will be converted to Java as:

UnnamedStreams.assignIn(StreamFactory.openFileStream("redirected_training37.txt", false, false));
ErrorManager.silentErrorEnable();
UnnamedStreams.safeInput().readField(intVar1);
UnnamedStreams.safeInput().resetCurrentLine();
ErrorManager.silentErrorDisable();

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(intVar1, frame0.widgetIntVar1())
};

frame0.display(elementList0);

message(ErrorManager.getErrorText(ErrorManager.numErrors()));

The input file for this test is:

12123231231323423423423423423

After application starts the screen is:

Note the option NO-ERROR is implementing as ErrorManager.silentErrorEnable()/Disable() execution block. Then the execution was not interrupted after error - very long integer is not fit the defined variable. The variable intVar does not receive the new value and we can check the error status via ERROR-STATUS:GET-MESSAGE and ERROR-STATUS:NUM-MESSAGES system handle to choose what to do next with the error encountered. This improves the flexibility of the application development process.

PUT statement

The statement is used to send the output of the one or several expressions value to the device other that terminal.

The 4GL syntax of the statement is:

PUT
   [ STREAM stream ]
   [ UNFORMATTED ]
   [     { expression
            [ FORMAT string ]
            [ { AT | TO } expression ]
         }
      | SKIP [ ( expression ) ]
      | SPACE [ ( expression ) ]
   ] ...

or

PUT [ STREAM stream ] CONTROL expression ...

The option description table:

4GL syntax Description Supported
STREAM stream Specifies the name of the stream. In case of not specified name unnamed stream is used. Yes
UNFORMATTED The same meaning as the UNFORMATTED option of the EXPORT statement. Yes
expression Variable, constant, field name or expression to output. Yes
FORMAT string Format string is used to perform the output like one used to define the variable. If this option is omitted the default value is used. Yes
expression Position for output the values for either starting the output or ending. Yes
SKIP [ (expression) ] Inserts the new line or several new lines. If the amount of the lines is not specified, or the amount is equal 0 the new line will be started only if the position is not already on a new line. Yes
SPACE [ (expression) ] The number of spaces to be inserted into the output. Yes
CONTROL expression Introducing special mode when the control characters are sending to the non-terminal output. Yes

The Java implementation of the statement is:

Stream.put(com.goldencode.p2j.util.FieldEntry[])

or

Stream.putControl(com.goldencode.p2j.util.FieldEntry[])

or

Stream.putUnformatted(com.goldencode.p2j.util.FieldEntry[])

What version will be used to implement the PUT statement depends on what option is used in particular example.

As note above the statement is used to perform output to the destination other than terminal screen. So it is not important whether to use the STREAM option or not but it is important to properly redirect the output. If the STREAM option is not used and the unnamed stream is used for this case the output should be redirected by the OUTPUT TO “file” statement. The following example shows the usage of the PUT statement with unnamed stream. The 4GL code is:

output to "redirected_training38.txt".
put "Hello from the put statement".
output close.

Will be converted to Java as:

UnnamedStreams.assignOut(StreamFactory.openFileStream("redirected_training38.txt", true, false));
UnnamedStreams.safeOutput().put(new FieldEntry[]
{
   new PutField("Hello from the put statement")
});
UnnamedStreams.closeOut();

When application completed the output file contains the following:

Hello from the put statement

The next option to consider is UNFORMATTED one. Use this case when it is required to convert all output data to the single character data and write it as single line. The output will be performed without any delimiter spaces. The 4GL code for this case is:

def var chVar1 as char initial "Another".
def var chVar2 as char initial "Put statement".
def var intVar as integer initial 123.
def var decVar as decimal initial 4567890.
...
put unformatted chVar1 chVar2 intVar decVar "Character_Constant" intVar+decVar.
output close.

will be converted to Java as:

integer intVar = new integer(123);
decimal decVar = new decimal(4567890);
...
      character chVar1 = new character("Another");
      character chVar2 = new character("Put statement");
...
         UnnamedStreams.safeOutput().putUnformatted(new FieldEntry[]
         {
            new PutField(chVar1),
            new PutField(chVar2),
            new PutField(intVar),
            new PutField(decVar),
            new PutField("Character_Constant"),
            new PutField(new PutExpr0())
         });
         UnnamedStreams.closeOut();

After application finishes the output file is:

AnotherPut statement1234567890Character_Constant4568013

Note there are neither delimiters nor quotes for character variables written. And the implementation Stream.putUnformatted() is used here instead of Stream.put(). Now we demonstrate the various output formatted options used with the PUT statement. The 4GL code is:

def var chVar1 as char initial "Another".
def var chVar2 as char initial "Put statement".
def var intVar as integer initial 123.
def var decVar as decimal initial 4567.
...
put chVar1 6.28 chVar2 intVar decVar "Character_Constant" intVar+decVar.
output close.

will be converted to Java as:

integer intVar = new integer(123);
decimal decVar = new decimal(4567);
...
      character chVar1 = new character("Another");
      character chVar2 = new character("Put statement");
...
         UnnamedStreams.safeOutput().put(new FieldEntry[]
         {
            new PutField(chVar1),
            new PutField(6.28),
            new PutField(chVar2),
            new PutField(intVar),
            new PutField(decVar),
            new PutField("Character_Constant"),
            new PutField(new PutExpr0())
         });
         UnnamedStreams.closeOut();
...
private class PutExpr0
extends GenericExpression
{
   public BaseDataType resolve()
   {
      return plus(intVar, decVar);
   }
}

After application finished the output file is:

Another       6.28Put stat       123  4,567.00Character_Constant  4,690.00

Consider the results. We have used the formatted output and for the cases where the value does not fit the default format - the value is truncating. Also we can see here the ability to output the variable, the constants both the integer and character and the expression evaluating at run time.

The next option we consider here is the FORMAT one. It uses to override the default formatting style used with the PUT statement. For example if we want to output the very long string variable the code can be:

def var chVar1 as char initial "Very long string variable. Truncated if default format is used.".
...
put chVar1 skip chVar1 format "x(65)".

converting to Java as:

character chVar1 = new character("Very long string variable. Truncated if default format is used.");
...
   UnnamedStreams.safeOutput().put(new FieldEntry[]
   {
      new PutField(chVar1),
      new SkipField(),
      new PutField(chVar1, "x(65)")
   });

After application run the output file is:

Very lon
Very long string variable. Truncated if default format is used.

Note we used the same variable two times. The first one does not have the FORMAT option and then use the default format “x(8)” for character variable. And this output has been truncated. The second case uses the redefined format option to be able the variable to be outputted without any truncating.

The next example demonstrates the repositioning inside the PUT statement within the same line. It is possible by using AT or TO options. Consider the following 4GL code:

def var chEvar as character initial "2.718282".
...
put "1234567890123456789012345678901234567890" skip chEvar at 5.

converting to Java as:

character chEvar = new character("2.718282");
...
   UnnamedStreams.safeOutput().put(new FieldEntry[]
   {
      new PutField("1234567890123456789012345678901234567890"),
      new SkipField(),
      new PutField(chEvar, true, 5)
   });

After running the output file is:

1234567890123456789012345678901234567890
    2.718282

Note the starting position of the character variable is 5 steps to the right from the beginning of the line. This is the AT case repositioning. The similar effect gives the TO case option. Let's modify our example the following way:

def var chEvar as character initial "2.718282".
...
put "1234567890123456789012345678901234567890" skip chEvar to 15.

will be converted to Java as:

character chEvar = new character("2.718282");
...
   UnnamedStreams.safeOutput().put(new FieldEntry[]
   {
      new PutField("1234567890123456789012345678901234567890"),
      new SkipField(),
      new PutField(chEvar, false, 15)
   });

Now after application completes the output file become:

1234567890123456789012345678901234567890
       2.718282

The idea of the TO option can be easily understood from the picture above. We have to make the output the way the last symbol having the position specified in TO option above. Also note the difference between PutField() constructors used in the recent two examples. The second parameter is logical start option. In case of using AT option it is equal to TRUE but for TO option it is equal to FALSE.

Now consider to options used to insert spaces into the output result. As we used in the examples above the SKIP option allow to insert the one or more new lines into the output file. The number passing to the option defines the number of the lines to be inserted. But the value of the 0 as line numbers is the same as usage of the SKIP option without any numbers. And this does mean starting the new line if the cursor is not already on the new line. Consider the following example:

put "This is the first line." skip(5) "And this is after skip 5 option.".

Converting to Java as:

UnnamedStreams.safeOutput().put(new FieldEntry[]
{
   new PutField("This is the first line."),
   new SkipField(5),
   new PutField("And this is after skip 5 option.")
});

After application run the output file is:

This is the first line.

And this is after skip 5 option.

So we have skipped over the next 5 lines before new line to be written. Now let's demonstrate the usage of the SKIP usage. Consider the following 4GL example:

put "This is the first line." skip(0) skip "And this is after 2 calls of skip(0).".

Will be converted to Java as:

UnnamedStreams.safeOutput().put(new FieldEntry[]
{
   new PutField("This is the first line."),
   new SkipField(0),
   new SkipField(),
   new PutField("And this is after 2 calls of skip(0).")
});

After application starts the output file is:

This is the first line.
And this is after 2 calls of skip(0).

Note the we have started the new line after the first skip call. Then we call the skip the second time and we do not start the new line because we already on the beginning of the new line. The next option to format the output is the usage of the SPACE parameter. Without this tool there will be no extra spaces between the output data values. Consider the following 4GL example:

put "1234567890123456789012345678901234567890" 
   skip "One" space "Two" space(0) "Three" space(1)
        "Four" space(2) "Five" space(3) "Six".

will be converted to Java as:

UnnamedStreams.safeOutput().put(new FieldEntry[]
{
   new PutField("1234567890123456789012345678901234567890"),
   new SkipField(),
   new PutField("One"),
   new SpaceField(),
   new PutField("Two"),
   new SpaceField(0),
   new PutField("Three"),
   new SpaceField(1),
   new PutField("Four"),
   new SpaceField(2),
   new PutField("Five"),
   new SpaceField(3),
   new PutField("Six")
});

After application finished the output file is:

1234567890123456789012345678901234567890
One TwoThree Four  Five   Six

Note the reaction of the various SPACE values. SPACE without the number gives one space. SPACE gives no spaces between. SPACE where n > 0 inserts the respective number of space characters between data entries.

The other possible way to use the PUT statement is to send the control sequence to the device other than terminal. The option CONTROL should be used for this purpose. Consider the following 4GL sample:

put control "~033E" NULL(5).

Sends ESC E and 5 null characters. Will be converted to Java as:

UnnamedStreams.safeOutput().putControl(new FieldEntry[]
{
   new PutField("\033E"),
   new NullField(5)
});

After application finished the output file will be the following:

Note this is binary file not character as in previous examples. And the new implementation Stream.putControl() is used here instead of the Stream.put() as in most of the examples above.


© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.