Record Layout Layout Matcher
When performing a file comparison or conversion, Ianus must be aware of the record layout of the files being processed.
There are cases where a file may have more than one layout and programs decides what layout to use depending on data contained on the record.
The following example shows a case where the record may have two layouts depending on the content of one or more fields:
01 CUSTOMERS-INFO.
03 RECORD-TYPE PIC X.
88 TYPE-ID VALUE 'I'.
88 TYPE-ADDRESS VALUE 'A'.
03 GENERIC-RECORD.
05 FILLER PIC X(79).
03 CUSTOMER-ID REDEFINES GENERIC-RECORD.
05 ACCOUNT PIC 9(8) COMP-3.
05 SURNAME PIC X(32).
05 FIRSTNAME PIC X(32).
05 DATE-OF-BIRTH PIC 9(8) COMP.
03 CUSTOMER-ADDRESS REDEFINES GENERIC-RECORD.
05 ADDRESS1 PIC X(32).
05 ADDRESS2 PIC X(32).
05 COUNTRY-CODE PIC X(2).
As you can read the record has a REDEFINES clause, meaning that the layout may vary. In the specific case, the content of the field RECORD-TYPE drives the selection of the specific layout.
So, to allow Ianus to compare the content of a file with multiple layout, the first thing to do is to code the layouts. As Ianus has no REDEFINES concept, each layout must be explicitly coded. For the example shown above, two layouts will be needed, both including the fixed part and the respective variable part.
Once the layouts have been defined, rules for the layout identification must be provided. Those rules will be invoked for all records (both sides), providing the current record data for the rules to determine the layout to be used in the comparison.
The rules are coded in a .NET class implementing the HPE.Ianus.Scripting.ILayoutMatch
interface:
namespace HPE.Ianus.Scripting
{
public interface ILayoutMatch
{
Layout Match(LoggerFacade log,
Side side,
Dictionary<string, Layout> layouts,
Record record,
EncodingType encoding);
}
}
Therefore the class must implement at least the method Match which purpose is to return the selected layout.
The method Match receives the following input parameters:
Parameter | Description |
---|---|
log | LoggerFacade object to write entries in the job log |
side | Side from which of the record is read |
layouts | Layouts dictionary |
record | Record data |
encoding | Default encoding of the repository containing the file (ASCII or EBCDIC) |
The script can examine the value of the fields defined in the different layouts to determine the layout to use. Those fields can be accessed directly through the layouts dictionary.
For example, to access the value of field RECORD-TYPE in the layout CUSTOMER-ID, the script can access the layout dictionary as follows:
Layout l = layouts["CUSTOMER-ID"];
if ((string)(l["RECORD-TYPE"].Value == “I”)
When accessed through the Value property of the layout field, is already converted in a C# data type and encoded in ASCII, also for EBCDIC fields. As alternative, in case one needs to examine the actual binary content of the record, it is possible to use the record object provided.
Once identified the desired layout, the script must return the layout selected (as obtained from the layouts object). If no layout is identified, the script should return null.
As alternative, the script may signal an error condition by raising the exception HPE.Ianus.Scripting.MatchException
.
Note
Match classed can also be used for dynamic field enablement. See Example 2.
Example 1
The following example shows the layouts and match definition for the example above:
The Layout
<layout name="CUSTOMER-ID">
<field name="RECORD-TYPE" type="char" length="1"/>
<field name="ACCOUNT" type="packed" length="5" signed="false"/>
<field name="SURNAME" type="char" length="32"/>
<field name="FIRSTNAME" type="char" length="32"/>
<field name="DATE-OF-BIRTH" type="integer" length="4" signed="false"/>
</layout>
<layout name="CUSTOMER-ADDRESS">
<field name="RECORD-TYPE" type="char" length="1"/>
<field name="ADDRESS1" type="char" length="32"/>
<field name="ADDRESS2" type="char" length="32"/>
<field name="COUNTRY-CODE" type="char" length="2"/>
</layout>
The Match class
using System.Collections.Generic;
using HPE.Ianus;
using HPE.Ianus.Log;
using HPE.Ianus.File;
using HPE.Ianus.Scripting;
namespace TestScript
{
public class CUSTOMERMatcher : HPE.Ianus.Scripting.ILayoutMatch
{
public Layout Match(LoggerFacade log,
Side side,
Dictionary<string, Layout> layouts,
Record record,
EncodingType e)
{
Layout l = layouts["CUSTOMER-ID"];
switch ((string)(l["RECORD-TYPE"].Value))
{
case 'I':
return layouts["CUSTOMER-ID"];
case 'A':
return layouts["CUSTOMER-ADDRESS"];
case 'X':
throw new ScriptException(
"record type 'X' is not supposed to be in the file"
);
}
return null;
}
}
}
Example 2
The following example shows how to use the match class to enable specific fields depending on record content.
The Script
<filecompare name="COMPARE" left="REPOEBCDIC" right="REPOBGLA" mode="layout" >
<left recfmt="fixed" reclen="80">IANUS.TEST.MULTIRED.EBCDIC.DAT</left>
<right recfmt="fixed" reclen="80">IANUS.TEST.MULTIRED.ASCII.DAT</right>
<layouts>
<layout type="cobol" format="fixed">
01 multi-rec.
03 rec-type pic 99.
03 rec-data pic x(78).
03 rec-01 redefines rec-data.
05 rec-01-a pic x(8).
05 rec-01-b pic 9(8).
05 rec-01-c pic x(8).
03 rec-02 redefines rec-data.
05 rec-02-a pic x(8).
05 rec-02-b pic S9(8) comp.
05 rec-02-c pic S9(8) comp-3.
05 rec-02-d pic x(10).
03 rec-03 redefines rec-data.
05 rec-03-a pic x(8).
05 rec-03-b pic S9(8) comp.
05 rec-03-c pic S9(8).
05 rec-03-d pic x(8).
</layout>
<match>
<include type="text">MULTIRED.cs</include>
</match>
</layouts>
</filecompare>
The Match class
using System.Collections.Generic;
using System.Linq;
using HPE.Ianus;
using HPE.Ianus.Log;
using HPE.Ianus.File;
using HPE.Ianus.Scripting;
namespace TestScript
{
public class RedefSelector : HPE.Ianus.Scripting.ILayoutMatch
{
public Layout Match(LoggerFacade log,
Side side,
Dictionary<string, Layout> layouts,
Record record,
EncodingType e)
{
Layout l = layouts[layouts.First().Key];
switch ((long)(l["REC-TYPE"].Value))
{
case 1:
l.Enable("REC-01", true, true);
break;
case 2:
l.Enable("REC-02", true, true);
break;
case 3:
log.Info("tipo 3");
l.Enable("REC-03", true, true);
break;
default:
log.Error("Record type {0} not recognized", l["REC-TYPE"].Value);
return null;
}
return l;
}
}
}