How to input / output IBM mainframe files in Java?

How to input / output IBM mainframe files in Java?

Inputting and outputting IBM mainframe files in Java can be a hassle, as described below.

So, when I was thinking about whether to make it myself, I found that a very useful utility class is provided as open source, so I will introduce how to use it. ** * Although it is convenient, there is little information in Japanese. It's a waste. ** </ font>

IBM Toolbox for Java From the name, it feels like THE mainframe.

Installation and setup

  • Download the ZIP file containing the OSS version of the jar (for example, jtopen_9_3.zip) from the download page below.
  • Unzip the "jtopen_9_3.zip" file, take out the "lib \ java8 \ jt400.jar" jar file, and put it in the classpath.

Information sources about programming, etc.

Requirements definition for inputting / outputting IBM mainframe files in Java

Before introducing the tool, I will briefly explain what you expect from this tool, that is, define the requirements, and show how to use the tool according to these requirements.

  1. I want to define the record format definition used for input / output of IBM mainframe files outside the Java program, and separate records and items according to the definition.
  2. I also want to perform character code conversion.
  3. I want to treat the so-called binary coded decimal numbers of signed packed decimal numbers and unsigned packed decimal numbers as Big Decimal.

Example: IBM mainframe file input / output in Java

It is assumed that the following IBM mainframe file record format is input / output in Java.

level Data item name Mold Number of digits accuracy Repetitive Part-Time Job Remarks
01 ORDER-REC 124 Order record
03 REC-NO B 4 2 Record number
03 ORDER-NO C 10 10 Order number
03 ZIP-CD C 8 8 Postal code
03 ADDRESS C 20 20 Street address(18 bytes for IBM Kanji code, 20 bytes for shift code)
03 PRODUCT-GR S 3 84 Product group items(OCCCURS 3 times)
05 PRODUCT-CD C 15 15 Product code
05 PRODUCT-AMT Z 5 2 5 Product weight(Zone decimal)
05 UNIT-PRC P 7 2 4 unit price(Pack decimal)
05 DISCOUNT-PRC P 7 2 4 Discount amount(Pack decimal)

Type legend:

Mold Description
B Binary type. If you write the above "record number" in COBOL, "S9"(4) COMP-Four". Since it is 4 digits in binary, the number of bytes is 2 bytes.
C Character type. If you write the above "order number" in COBOL, "X"(10)」。
S Group item. It bundles multiple data items.
P Packed decimal type. If you write the above "unit price" in COBOL, "S9"(7)V9(2) COMP-3」。
Z Zone decimal type. When the above "product weight" is written in COBOL, "9(5)V9(2)」。

For explanations of zoned decimal numbers (also called external decimal numbers and unpacked decimal numbers) and packed decimal numbers (internal decimal numbers), refer to the following URLs. https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%83%E3%82%AF10%E9%80%B2%E6%95%B0 https://www.ibm.com/support/knowledgecenter/ja/SSQ2R2_9.1.0/com.ibm.etools.cbl.win.doc/topics/cpari20e.htm#header_19

Create a record format definition file (RFML file) and place it on the classpath.

Create a record format definition file for the mainframe file and enable input / output of the file accordingly. With IBM ToolBox for Java, you can input and output files based on XML format RFML files as record format definition files. First, make it and put it on your classpath. This is to get this definition file from the classpath later from the Java program.

You can find the DTD for this XML file in Programming IBM Toolbox for Java. Notice where it says "ccsid =" 930 "". It is written at the root level and at "ADRESS". This specifies the character code of the mainframe, but if you want to specify the entire character code, write it at this root level. If you specify them individually, you can specify them for each data item.

ORDER-FILE.rfml(Mainframe record format definition file)


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE rfml SYSTEM "rfml.dtd">
<rfml version="4.0" ccsid="930">
  <struct name="PRODUCT-GR">
    <data name="PRODUCT-CD" type="char" length="15"/>
    <data name="PRODUCT-AMT" type="zoned" length="5" precision="2"/>
    <data name="UNIT-PRC" type="packed" length="7" precision="2"/>
    <data name="DISCOUNT-PRC" type="packed" length="7" precision="2"/>
  </struct>
  <recordformat name="ORDER01-REC">
    <data name="REC-NO" type="int" length="2"/>
    <data name="ORDER-NO" type="char" length="10"/>
    <data name="ZIP-CD" type="char" length="8"/>
    <data name="ADRESS" type="char" length="20" ccsid="930"/>
    <data name="PRODUCT-GR" type="struct" struct="PRODUCT-GR" count="3"/>
  </recordformat>
</rfml>

Mainframe file output part 1 (failure example)

The following is an example of failure. Structure and Array are not supported. In other words, group items and OCCURS cannot be used. what to do?

Mainframe file output part 1(Failure example)


    /**
     *Mainframe file output. <BR>
     *Outputs the mainframe file according to the record format definition of the RFML file.<BR>
     * {@code
      rec.setField("PRODUCT-GR", null);}At the place{@code
    com.ibm.as400.access.ExtendedIllegalArgumentException: name (PRODUCT-GR): Field was not found.
    }<BR>
     *I get an exception. The following API documentation states that Structure is not supported. Array is also not supported.<BR>
     * <a href=
     * "https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzahh/javadoc/com/ibm/as400/data/RecordFormatDocument.html">
     * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzahh/javadoc/com/ibm/as400/data/RecordFormatDocument.html</a>
     */
    @Test
    public void test01MfFileOutput() {
        //Find and read the record format definition from the classpath.
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument("jp.golden.cat.util.ORDER-FILE.rfml");
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }

        //File output with mainframe character code.
        File ioFile = new File("./data/ORDER_FILE.dat");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            Record rec = rfd.toRecordFormat("ORDER01-REC").getNewRecord();
            for (short i = 1; i < 11; i++) {
                rec.setField("REC-NO", new Short(i));
                rec.setField("ORDER-NO", "170423-123");
                rec.setField("ZIP-CD", "103-0028");
                rec.setField("ADRESS", "Yaesu, Chuo-ku, Tokyo");
                //↓ An exception occurs here, so comment it out.
                // rec.setField("PRODUCT-GR", null);
                //Byte array and output to file.
                byte[] buf = rec.getContents();
                StringUtil.dump("DEBUG:", buf, "");
                fos.write(buf);
            }
            fos.flush();
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

Mainframe file output part 2 (success example)

Group items and OCCURS cannot be used, so handle as follows.

  1. Group items and OCCURS do not behave as intended, so consider that part as a single byte array.
  2. Define the group item part in another record format definition and set a Java object there to make a byte array of MF character code.
    1. Set the above 2 to the above 1.

ORDER-FILE.rfml(Mainframe record format definition file. The group item part has a different record definition.)


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE rfml SYSTEM "rfml.dtd">
<rfml version="4.0" ccsid="930">
  <recordformat name="PRODUCT-GR">
    <data name="PRODUCT-CD" type="char" length="15"/>
    <data name="PRODUCT-AMT" type="zoned" length="5" precision="2"/>
    <data name="UNIT-PRC" type="packed" length="7" precision="2"/>
    <data name="DISCOUNT-PRC" type="packed" length="7" precision="2"/>
  </recordformat>
  <recordformat name="ORDER01-REC">
    <data name="REC-NO" type="int" length="2"/>
    <data name="ORDER-NO" type="char" length="10"/>
    <data name="ZIP-CD" type="char" length="8"/>
    <data name="ADRESS" type="char" length="20" ccsid="930"/>
    <data name="PRODUCT-B" type="byte" length="84"/>
  </recordformat>
</rfml>

Mainframe file output part 2(Success story)


    /**
     *Mainframe file output. <BR>
     *Outputs the mainframe file according to the record format definition of the RFML file.<BR>
     * 1.Since the group item and OCCURS do not behave as intended, that part is regarded as a byte array.<BR>
     * 2.Define the group item part in another record format definition and set a Java object there to make a byte array of MF character code.<br>
     *3. 3. Above 2 above 1.Set to.
     */
    @Test
    public void test02MfFileOutput() {
        //Find and read the record format definition from the classpath.
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument("jp.golden.cat.util.ORDER-FILE.rfml");
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }

        //File output with mainframe character code.
        File ioFile = new File("./data/ORDER_FILE.dat");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            Record rec = rfd.toRecordFormat("ORDER01-REC").getNewRecord();
            Record grp = rfd.toRecordFormat("PRODUCT-GR").getNewRecord();
            for (int i = 1; i < 11; i++) {
                rec.setField("REC-NO", new Short((short) i));
                rec.setField("ORDER-NO", "170423-123");
                rec.setField("ZIP-CD", "103-0028");
                rec.setField("ADRESS", "Yaesu, Chuo-ku, Tokyo");

                //The code for the OCCURS part of the group item.
                //A byte stream of the entire group item to which the item is transferred.
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                //Byte length of item transfer destination ÷ 1 Byte length of OCCURS(PRODUCT-GR)= Number of OCCC URS
                int occursCount = rec.getFieldAsBytes("PRODUCT-B").length / grp.getRecordLength();
                //OCCURS Repeat for a few minutes
                for (int j = 0; j < occursCount; j++) {
                    grp.setField("PRODUCT-CD", "ABCDEFGHIJ-000" + j);
                    grp.setField("PRODUCT-AMT", new BigDecimal("123.45"));
                    grp.setField("UNIT-PRC", new BigDecimal("+12345.67"));
                    grp.setField("DISCOUNT-PRC", new BigDecimal("-45.6"));

                    baos.write(grp.getContents());
                }
                //Set in a byte array of group items.
                rec.setField("PRODUCT-B", baos.toByteArray());

                //Byte array and output to file.
                byte[] buf = rec.getContents();
                StringUtil.dump("DEBUG:", buf, "");
                fos.write(buf);
            }
            fos.flush();
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

Mainframe file input

Since group items and OCCURS cannot be used, it corresponds with the image as done in the above file output.

Mainframe file input


    /**
     *Mainframe file input. <BR>
     *Enter the mainframe file according to the record format definition of the RFML file.<BR>
     * 1.Since the group item and OCCURS do not behave as intended, that part is regarded as a byte array.<BR>
     * 2.Define the group item part in another record format definition, set the byte array of MF character code there, and make it a Java object.<br>
     */
    @Test
    public void test03MfFileInput() {
        //Create a mainframe file.
        this.test02MfFileOutput();

        //Find and read the record format definition from the classpath.
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument("jp.golden.cat.util.ORDER-FILE.rfml");
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }

        //File input with mainframe character code.
        File ioFile = new File("./data/ORDER_FILE.dat");
        FileInputStream fis = null;
        try {
            Record rec = rfd.toRecordFormat("ORDER01-REC").getNewRecord();
            Record grp = rfd.toRecordFormat("PRODUCT-GR").getNewRecord();
            fis = new FileInputStream(ioFile);
            //Gets the number of bytes for one record and creates a byte array for the read buffer.
            byte[] recordBuf = new byte[rec.getRecordLength()];
            //There are no newline characters in the MF file. Therefore, the number of bytes in one record is read into the buffer.
            while (fis.read(recordBuf) != -1) {
                //Record from the byte array of the buffer.
                rec.setContents(recordBuf);

                System.out.print("[" + rec.getField("REC-NO"));
                System.out.print("],[" + rec.getField("ORDER-NO"));

                //The code for the OCCURS part of the group item.
                //A byte stream of the entire group item from which the item was obtained.
                ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) rec.getField("PRODUCT-B"));
                //Create a read buffer for 1 OCCURS.
                byte[] groupBuf = new byte[grp.getRecordLength()];
                while (bais.read(groupBuf) != -1) {
                    grp.setContents(groupBuf);
                    System.out.print("],[" + grp.getField("PRODUCT-CD"));
                    System.out.print("],[" + grp.getField("PRODUCT-AMT"));
                    System.out.print("],[" + grp.getField("UNIT-PRC"));
                    System.out.print("],[" + grp.getField("DISCOUNT-PRC"));
                }
                System.out.println("]");
            }
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

2017/04/19 (Wed): New findings:

rfd.toRecord ("ORDER-REC"). getRecordLength () raises the following exception: 「com.ibm.as400.data.PcmlException: Value is not set. Processing element 'ORDER-REC.REC-NO'.」 You probably can't get the Length because the Record object wasn't created inside the RecordFormatDocument object. I am modifying the sample code to create an object with getNewRecord () and then get the Length.

2017/04/19 (Wed): Bug fix:

I didn't set the buffer in the RecordFormatDocument object after reading it into the buffer. while (fis.read(buffer) != -1) { rfd.setValues("ORDER-REC", buffer);

2017/04/19 (Wed): Newly found (getValue () method trims without permission):

The following code is both methods that retrieve ORDER-NOTE, but with different results. (1) rfd.getValue("ORDER-REC.ORDER-NOTE") (2) rec.getField("ORDER-NOTE") In (1), the trailing space of String is deleted without permission. (2) remains as it is if there is a trailing space. (1) seems to call PcmlDataValues.toObject () internally. Among them, trimString.

PcmlDataValues.toObject()


//For Strings, trim blanks and nulls appropriately
if (dataType == PcmlData.CHAR) {
    newVal = trimString((String)newVal, getTrim());
}

getValue () is doing whatever it wants, so it's better to use the getField () method.

2017/04/23 (Sun): New findings:

AS400Array can write to RFML files, but when reading to RFML files, the OCCURS number is not reproduced. AS400Structre cannot write RFML files. An exception occurs when I try to read and use it as an RFML file.

2017/04/26 (Wed): I / O review of group items:

If Stream is used for file input / output, it is better to input / output the byte array of group items with Stream as a whole.

Other entire code used for confirmation

IBMToolboxTest.java


package jp.golden.cat.util;

import static org.junit.Assert.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharConversionException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import com.ibm.as400.access.AS400Array;
import com.ibm.as400.access.AS400Bin4;
import com.ibm.as400.access.AS400ByteArray;
import com.ibm.as400.access.AS400DataType;
import com.ibm.as400.access.AS400PackedDecimal;
import com.ibm.as400.access.AS400Structure;
import com.ibm.as400.access.AS400Text;
import com.ibm.as400.access.ArrayFieldDescription;
import com.ibm.as400.access.BinaryFieldDescription;
import com.ibm.as400.access.CharacterFieldDescription;
import com.ibm.as400.access.HexFieldDescription;
import com.ibm.as400.access.PackedDecimalFieldDescription;
import com.ibm.as400.access.Record;
import com.ibm.as400.access.RecordFormat;
import com.ibm.as400.data.RecordFormatDocument;
import com.ibm.as400.data.XmlException;

/**
 *Testing IBM Toolbox for Java.<BR>
 *
 * <H1>Installation and setup</H1><BR>
 *From the download page below, the ZIP file containing the OSS version of the jar(For example, jtopen_9_3.zip)Download.<br>
 * 「jtopen_9_3.zip\lib\java8\jt400.Put "jar" in the classpath.<br>
 * <br>
 * <H1>Information sources, etc.</H1> <BR>
 *IBM Toolbox for Java. An overview description provided by IBM. URL:<a href=
 * "https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzahh/page1.htm">https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzahh/page1.htm</a><BR>
 *Programming IBM Toolbox for Java. PDF file. Outline explanation of programming methods. URL:<a href=
 * "https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzahh/rzahhpdf.pdf">https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzahh/rzahhpdf.pdf</a><BR>
 * IBM Toolbox for Java API Document。URL:<a href=
 * "https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahh/javadoc/index.html">https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahh/javadoc/index.html</a><BR>
 * JTOpen: The Open Source version of the IBM Toolbox for
 *Java. An overview of the open source version. URL:<a href=
 * "http://jt400.sourceforge.net/">http://jt400.sourceforge.net/</a><BR>
 * JTOpen: The Open Source version of the IBM Toolbox for
 *Java download page. URL:<a href=
 * "https://sourceforge.net/projects/jt400/">https://sourceforge.net/projects/jt400/</a><BR>
 *The latest version download URL. URL:<a href=
 * "https://sourceforge.net/projects/jt400/files/latest/download?source=files">https://sourceforge.net/projects/jt400/files/latest/download?source=files</a><BR>
 *License for JTOpen and IBM Toolbox for Java. Both are "IBM Public License Version"
 * 1.0」。URL:<a href=
 * "https://opensource.org/licenses/ibmpl.php">https://opensource.org/licenses/ibmpl.php</a><BR>
 */
public class IBMToolboxTest {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    /**
     *Mainframe file output. <BR>
     *Outputs the mainframe file according to the record format definition of the RFML file.<BR>
     * {@code
      rec.setField("PRODUCT-GR", null);}At the place{@code
    com.ibm.as400.access.ExtendedIllegalArgumentException: name (PRODUCT-GR): Field was not found.
    }<BR>
     *I get an exception. The following API documentation states that Structure is not supported. Array is also not supported.<BR>
     * <a href=
     * "https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzahh/javadoc/com/ibm/as400/data/RecordFormatDocument.html">
     * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzahh/javadoc/com/ibm/as400/data/RecordFormatDocument.html</a>
     */
    @Test
    public void test01MfFileOutput() {
        //Find and read the record format definition from the classpath.
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument("jp.golden.cat.util.ORDER-FILE.rfml");
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }

        //File output with mainframe character code.
        File ioFile = new File("./data/ORDER_FILE.dat");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            Record rec = rfd.toRecordFormat("ORDER01-REC").getNewRecord();
            for (short i = 1; i < 11; i++) {
                rec.setField("REC-NO", new Short(i));
                rec.setField("ORDER-NO", "170423-123");
                rec.setField("ZIP-CD", "103-0028");
                rec.setField("ADRESS", "Yaesu, Chuo-ku, Tokyo");
                //↓ An exception occurs here, so comment it out.
                // rec.setField("PRODUCT-GR", null);
                //Byte array and output to file.
                byte[] buf = rec.getContents();
                StringUtil.dump("DEBUG:", buf, "");
                fos.write(buf);
            }
            fos.flush();
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     *Mainframe file output. <BR>
     *Outputs the mainframe file according to the record format definition of the RFML file.<BR>
     * 1.Since the group item and OCCURS do not behave as intended, that part is regarded as a byte array.<BR>
     * 2.Define the group item part in another record format definition and set a Java object there to make a byte array of MF character code.<br>
     *3. 3. Above 2 above 1.Set to.
     */
    @Test
    public void test02MfFileOutput() {
        //Find and read the record format definition from the classpath.
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument("jp.golden.cat.util.ORDER-FILE.rfml");
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }

        //File output with mainframe character code.
        File ioFile = new File("./data/ORDER_FILE.dat");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            Record rec = rfd.toRecordFormat("ORDER01-REC").getNewRecord();
            Record grp = rfd.toRecordFormat("PRODUCT-GR").getNewRecord();
            for (int i = 1; i < 11; i++) {
                rec.setField("REC-NO", new Short((short) i));
                rec.setField("ORDER-NO", "170423-123");
                rec.setField("ZIP-CD", "103-0028");
                rec.setField("ADRESS", "Yaesu, Chuo-ku, Tokyo");

                //The code for the OCCURS part of the group item.
                //A byte stream of the entire group item to which the item is transferred.
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                //Byte length of item transfer destination ÷ 1 Byte length of OCCURS(PRODUCT-GR)= Number of OCCC URS
                int occursCount = rec.getFieldAsBytes("PRODUCT-B").length / grp.getRecordLength();
                //OCCURS Repeat for a few minutes
                for (int j = 0; j < occursCount; j++) {
                    grp.setField("PRODUCT-CD", "ABCDEFGHIJ-000" + j);
                    grp.setField("PRODUCT-AMT", new BigDecimal("123.45"));
                    grp.setField("UNIT-PRC", new BigDecimal("+12345.67"));
                    grp.setField("DISCOUNT-PRC", new BigDecimal("-45.6"));

                    baos.write(grp.getContents());
                }
                //Set in a byte array of group items.
                rec.setField("PRODUCT-B", baos.toByteArray());

                //Byte array and output to file.
                byte[] buf = rec.getContents();
                StringUtil.dump("DEBUG:", buf, "");
                fos.write(buf);
            }
            fos.flush();
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     *Mainframe file input. <BR>
     *Enter the mainframe file according to the record format definition of the RFML file.<BR>
     * 1.Since the group item and OCCURS do not behave as intended, that part is regarded as a byte array.<BR>
     * 2.Define the group item part in another record format definition, set the byte array of MF character code there, and make it a Java object.<br>
     */
    @Test
    public void test03MfFileInput() {
        //Create a mainframe file.
        this.test02MfFileOutput();

        //Find and read the record format definition from the classpath.
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument("jp.golden.cat.util.ORDER-FILE.rfml");
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }

        //File input with mainframe character code.
        File ioFile = new File("./data/ORDER_FILE.dat");
        FileInputStream fis = null;
        try {
            Record rec = rfd.toRecordFormat("ORDER01-REC").getNewRecord();
            Record grp = rfd.toRecordFormat("PRODUCT-GR").getNewRecord();
            fis = new FileInputStream(ioFile);
            //Gets the number of bytes for one record and creates a byte array for the read buffer.
            byte[] recordBuf = new byte[rec.getRecordLength()];
            //There are no newline characters in the MF file. Therefore, the number of bytes in one record is read into the buffer.
            while (fis.read(recordBuf) != -1) {
                //Record from the byte array of the buffer.
                rec.setContents(recordBuf);

                System.out.print("[" + rec.getField("REC-NO"));
                System.out.print("],[" + rec.getField("ORDER-NO"));

                //The code for the OCCURS part of the group item.
                //A byte stream of the entire group item from which the item was obtained.
                ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) rec.getField("PRODUCT-B"));
                //Create a read buffer for 1 OCCURS.
                byte[] groupBuf = new byte[grp.getRecordLength()];
                while (bais.read(groupBuf) != -1) {
                    grp.setContents(groupBuf);
                    System.out.print("],[" + grp.getField("PRODUCT-CD"));
                    System.out.print("],[" + grp.getField("PRODUCT-AMT"));
                    System.out.print("],[" + grp.getField("UNIT-PRC"));
                    System.out.print("],[" + grp.getField("DISCOUNT-PRC"));
                }
                System.out.println("]");
            }
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * COMP-Java object from 4(Integer)Test to convert to.
     */
    @Test
    public void test01() {
        //Input value(MF byte data)
        //Endianness is big endian in both IBM mainframe and Java, so you don't have to worry about byte order.
        byte[] mfData = new byte[] { 0x0A, 0x0B, 0x0C, 0x0D };
        StringUtil.dump("test01:input   :", mfData, "");

        // COMP-Java object from 4(Integer)Convert to.
        AS400Bin4 comp4 = new AS400Bin4();
        int actual = ((Integer) comp4.toObject(mfData, 0)).intValue();
        System.out.println("test01:actual  :" + actual);

        //Expected value creation.
        int expected = 168496141;
        System.out.println("test01:expected:" + expected);

        //0x0A0B0C0D is the decimal number 164896141.
        assertEquals(expected, actual);
    }

    /**
     *Java object(Integer)From COMP-Test to convert to 4.
     */
    @Test
    public void test02() {
        //Input value(Java object)
        Integer javaObject = new Integer(168496141);
        System.out.println("test02:input   :" + javaObject);

        //Java object(Integer)From COMP-Convert to 4.
        AS400Bin4 comp4 = new AS400Bin4();
        byte[] actual = comp4.toBytes(javaObject);
        StringUtil.dump("test02:actual  :", actual, "");

        //Expected value creation.
        byte[] expected = new byte[] { 0x0A, 0x0B, 0x0C, 0x0D };
        StringUtil.dump("test02:expected:", expected, "");

        // test
        assertArrayEquals(expected, actual);
    }

    /**
     *PIC X format EBCDIC to MS932(SJIS)Test to convert to. <BR>
     *
     * IBM Globalization - Coded character set identifiers.UR:<a href=
     * "https://www-01.ibm.com/software/globalization/ccsid/ccsid_registered.html">https://www-01.ibm.com/software/globalization/ccsid/ccsid_registered.html</a><br>
     *
     * IBM Globalization - Coded character set identifiers - CCSID
     * 5035。URL:<a href=
     * "https://www-01.ibm.com/software/globalization/ccsid/ccsid5035.html">https://www-01.ibm.com/software/globalization/ccsid/ccsid5035.html</a><br>
     *
     *Alternate Unicode conversion table for CCSID 5035.URL:<a href=
     * "https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_9.5.0/com.ibm.db2.luw.admin.nls.doc/doc/c0022611.html">https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_9.5.0/com.ibm.db2.luw.admin.nls.doc/doc/c0022611.html</a><br>
     *
     * 79.CCSID 5035 to enter lowercase letters.URL:<a href=
     * "http://www.as400-net.com/tips/environment/79.html">http://www.as400-net.com/tips/environment/79.html</a><br>
     *
     * CCSID=930 「4370 UDC
     *5026 superset of Japanese Katakana and Kanji including characters. Half-width kana priority(Half-width lowercase letters cannot be mixed)。<BR>
     * CCSID=939 「4370 UDC
     *5035 Super Set of Japanese Latin Kanji including characters. Half-width lowercase letters have priority(Half-width kana characters cannot be mixed)。<BR>
     */
    @Test
    public void test03() {
        //MF character code, CCSID=930 「4370 UDC
        //5026 superset of Japanese Katakana and Kanji including characters. Half-width kana priority(Half-width lowercase letters cannot be mixed)Byte array.
        // "123AαB"=0xF1F2F3C10E41410FC281。
        //Half-width katakana"A"Is CCSID=930 is 0x81. This is the CCSID=It's 939 0x56.
        //Double-byte characters"α"Is the MF character code 0X4141. Shift this out(0x0E)And shift in(0x0F)Deck, 0x0E41410F.
        byte[] mfData = new byte[] { (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xC1, 0x0E, 0x41, 0x41, 0x0F,
                (byte) 0xC2, (byte) 0x81 };
        StringUtil.dump("test03:input   :", mfData, "");
        System.out.println("test03:number of input bytes:" + mfData.length);

        // CCSID=930 「4370 UDC
        //5026 superset of Japanese Katakana and Kanji including characters. Half-width kana priority(Half-width lowercase letters cannot be mixed)Byte array of
        //Convert to Unicode.
        int ccsid = 930;
        AS400Text xtype = new AS400Text(mfData.length, ccsid);
        String unicodeString = (String) xtype.toObject(mfData);
        System.out.println("test03:unicodeString Number of bytes:" + unicodeString.length());
        StringUtil.dump("test03:unicodeString:", unicodeString.getBytes(), "");

        //Unicode to MS932(SJIS)Convert to.
        String encoding = "MS932";
        String actual = this.convertString(unicodeString, encoding);
        System.out.println("test03:actual  :" + actual);
        //Shift out(0x0E)And shift in(0x0F)Because there is no, it is 2 bytes less.
        System.out.println("test03:actual number of bytes:" + this.getStringByteLength(actual, encoding));
        StringUtil.dump("test03:actual  :", this.getStringBytes(actual, encoding), "");

        //Make an expected value.
        String expected = this.convertString("123AαB", encoding);
        System.out.println("test03:expected:" + expected);
        //Shift out(0x0E)And shift in(0x0F)Because there is no, it is 2 bytes less.
        System.out.println("test03:expected number of bytes:" + this.getStringByteLength(expected, encoding));
        StringUtil.dump("test03:expected:", this.getStringBytes(expected, encoding), "");

        // test
        assertEquals(expected, actual);
    }

    /**
     *Character code conversion.
     *
     * @param from The source string.
     * @param encoding Character code after conversion.
     * @return The converted character string.
     */
    String convertString(String from, String encoding) {
        String res = null;
        try {
            res = new String(getStringBytes(from, encoding), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return res;
    }

    /**
     *Character code conversion and return as a byte array.
     *
     * @param from The source string.
     * @param encoding Character code after conversion.
     * @return Byte array after conversion.
     */
    byte[] getStringBytes(String from, String encoding) {
        byte[] res = null;
        try {
            res = from.getBytes(encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return res;
    }

    /**
     *Returns the number of bytes after character code conversion.
     *
     * @param from The source string.
     * @param encoding Character code after conversion.
     * @return The number of bytes after character code conversion.
     */
    int getStringByteLength(String from, String encoding) {
        return getStringBytes(from, encoding).length;
    }

    /**
     *Complex data type conversion test.
     */
    @Test
    public void test04() {
        //Java composite object
        Object[] javaObjects = {
                new Integer(88),
                new byte[] { 0x41 },
                new BigDecimal("-12.34"),
                "ABCDE"
        };

        //MF composite data definition
        AS400DataType[] mfDataType = {
                new AS400Bin4(),
                new AS400ByteArray(4),
                new AS400PackedDecimal(4, 2),
                new AS400Text(10)
        };

        //Generates a converter with the MF composite data definition specified by the argument.
        AS400Structure converter = new AS400Structure(mfDataType);

        //Generates a byte array with MF character code conversion from a Java composite object.
        byte[] outAS400 = converter.toBytes(javaObjects);
        StringUtil.dump("test04:", outAS400, "");

        Object[] outJavaObjects = (Object[]) converter.toObject(outAS400, 0);
        System.out.println("[" + outJavaObjects[0] + "]");
        StringUtil.dump("[", (byte[]) outJavaObjects[1], "]");    //If 0x41 is set for 4 bytes on the receiving side, it will be set left-justified. Fill the rest with 0x00.
        System.out.println("[" + outJavaObjects[2] + "]");
        System.out.println("[" + outJavaObjects[3] + "]");  //If you set 5 characters of ABCDE for 10 bytes on the receiving side, it will be set with the left nail. The rest is space.

        // test
        assertEquals(88, outJavaObjects[0]);
        assertArrayEquals(new byte[] { 0x41, 0x00, 0x00, 0x00 }, (byte[]) outJavaObjects[1]);
        assertEquals(new BigDecimal("-12.34"), outJavaObjects[2]);
        assertEquals("ABCDE     ", outJavaObjects[3]);
    }

    /**
     *A test that creates an MF record format definition object and sets its values accordingly.
     */
    @Test
    public void test05() {
        //Item definition
        BinaryFieldDescription bfd = new BinaryFieldDescription(new AS400Bin4(), "REC-NO");
        CharacterFieldDescription cfd1 = new CharacterFieldDescription(new AS400Text(10), "ORDER-NO");
        CharacterFieldDescription cfd2 = new CharacterFieldDescription(new AS400Text(20), "ORDER-NOTE");

        //Record format definition
        RecordFormat rf = new RecordFormat();
        rf.addFieldDescription(bfd);
        rf.addFieldDescription(cfd1);
        rf.addFieldDescription(cfd2);

        //Generate a record and set the value of Java object.
        Record rec = rf.getNewRecord();
        rec.setField("REC-NO", new Integer(1));
        rec.setField("ORDER-NO", "170415-123");
        rec.setField("ORDER-NOTE", "HELLO WORLD");

        //Get the MF byte array.
        byte[] actual = null;
        try {
            actual = rec.getContents();
        } catch (CharConversionException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        //Expected value.
        byte[] expected = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xF1, (byte) 0xF7,
                (byte) 0xF0, (byte) 0xF4, (byte) 0xF1, (byte) 0xF5, (byte) 0x60, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3,
                (byte) 0xC8, (byte) 0xC5, (byte) 0xD3, (byte) 0xD3, (byte) 0xD6, (byte) 0x40, (byte) 0xE6, (byte) 0xD6,
                (byte) 0xD9, (byte) 0xD3, (byte) 0xC4, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
                (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40 };
        StringUtil.dump("test05:actual  :", actual, "");
        StringUtil.dump("test05:expected:", expected, "");

        // test
        assertArrayEquals(expected, actual);
    }

    /**
     *Generate MF record format definition object and input / output test of EBCIDIC binary file accordingly.
     */
    @Test
    public void test06() {
        RecordFormat rf = this.createOrderRecordFormat();
        Record rec = rf.getNewRecord();

        File ioFile = new File("./data/AS400.dat");

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            //10 record generation
            for (int i = 1; i < 11; i++) {
                rec.setField("REC-NO", new Integer(i));
                rec.setField("AMOUNT", new BigDecimal("-12.34"));
                rec.setField("ORDER-NO", "170415-123");
                rec.setField("ORDER-NOTE", "HELLOαWORLD");
                //OutputStream can be specified as an argument of getContents.
                //Idiom-wise os.write(rec.getContents())I use it a lot, so I'll go with this.
                fos.write(rec.getContents());
            }
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (CharConversionException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        FileInputStream fis = null;
        try {
            fis = new FileInputStream(ioFile);
            rec = rf.getNewRecord();
            //Get the number of bytes for one record and generate a byte array.
            int len = rec.getRecordLength();
            byte[] buffer = new byte[len];

            //There are no newline characters in the MF file. Therefore, the number of bytes in one record is read into the buffer.
            while (fis.read(buffer) != -1) {
                rec.setContents(buffer);
                StringUtil.dump("test06:", buffer, "");
                System.out.print(rec.getField("REC-NO"));
                System.out.print("," + rec.getField("ORDER-NO"));
                System.out.print("," + rec.getField("AMOUNT"));
                System.out.println("," + rec.getField("ORDER-NOTE"));
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     *Generate an MF record format definition object from the MF record format definition file and test the EBCDIC binary file output accordingly..
     *
     * @throws XmlException Record format exception
     * @throws IOException File IO Exception
     */
    @Test
    public void test07() throws XmlException, IOException {
        //Generate MF record format definition.
        RecordFormat rf = this.createOrderRecordFormat();

        //Save the MF record format definition on the classpath.
        this.saveRecordFormat(rf, new File("src/test/java/jp/golden/cat/util/ORDER.rfml"));

        //Find and read the MF record format definition from the classpath.
        RecordFormatDocument rfd = this.loadRecordFormatDocument("jp.golden.cat.util.ORDER.rfml");

        //Write in EBCDIC.
        File ioFile = new File("./data/AS400_2.dat");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            //10 record generation
            for (int i = 1; i < 11; i++) {
                //I have a habit of specifying items. "Record format definition name.Specify the item in "Item name".
                rfd.setValue("ORDER-REC.REC-NO", new Integer(i));
                rfd.setValue("ORDER-REC.AMOUNT", new BigDecimal("-12.34"));
                rfd.setValue("ORDER-REC.ORDER-NO", "170415-123");
                rfd.setValue("ORDER-REC.ORDER-NOTE", "HELLOαWORLD");
                fos.write(rfd.toByteArray("ORDER-REC"));
            }
            fos.flush();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     *Generate an MF record format definition object from the MF record format definition file and test the EBCDIC binary file input accordingly..
     *
     * @throws XmlException Record format exception
     * @throws IOException File IO Exception
     */
    @Test
    public void test08() throws XmlException, IOException {
        //Find and read the MF record format definition from the classpath.
        RecordFormatDocument rfd = this.loadRecordFormatDocument("jp.golden.cat.util.ORDER.rfml");

        //Read the EBCDIC file.
        File ioFile = new File("./data/AS400_2.dat");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(ioFile);

            // rfd.toRecord("ORDER-REC").getRecordLength()Will raise the following exception.
            // com.ibm.as400.data.PcmlException: Value is not set. Processing
            // <data> element 'ORDER-REC.REC-NO'.
            //Probably because the Record object is not created in the RecordFormatDocument object, it seems that the Length cannot be obtained.
            //Therefore, getNewRecord()Create an object with and then get the Length.
            // int len = rfd.toRecord("ORDER-REC").getRecordLength();

            //Get the number of bytes for one record and generate a byte array.
            int len = rfd.toRecordFormat("ORDER-REC").getNewRecord().getRecordLength();
            byte[] buffer = new byte[len];

            //There are no newline characters in the MF file. Therefore, the number of bytes in one record is read into the buffer.
            while (fis.read(buffer) != -1) {
                rfd.setValues("ORDER-REC", buffer);

                StringUtil.dump("test08:", rfd.toByteArray("ORDER-REC"), "");

                System.out.print("[" + rfd.getValue("ORDER-REC.REC-NO"));
                System.out.print("],[" + rfd.getValue("ORDER-REC.ORDER-NO"));
                System.out.print("],[" + rfd.getValue("ORDER-REC.AMOUNT"));
                System.out.print("],[" + rfd.getValue("ORDER-REC.ORDER-NOTE"));
                System.out.println("]");

                Record rec = rfd.toRecord("ORDER-REC");
                System.out.print("[" + rec.getField("REC-NO"));
                System.out.print("],[" + rec.getField("ORDER-NO"));
                System.out.print("],[" + rec.getField("AMOUNT"));

                //The following code is both ORDER-This is a method to retrieve NOTE, but the result is different.
                // (1) rfd.getValue("ORDER-REC.ORDER-NOTE")
                // (2) rec.getField("ORDER-NOTE")
                // (1)The trailing space of the String is deleted without permission.(2)Will remain intact if there is a trailing space.
                // (1)Internally PcmlDataValues.toObject()Seems to be calling. Among them, trimString.
                /*
                 * //For Strings, trim blanks and nulls appropriately if
                 * (dataType == PcmlData.CHAR) { newVal = trimString((String)
                 * newVal, getTrim()); }
                 */
                // getValue()Is doing selfish things, so getField()It is better to use the method.
                System.out.print("],[" + rec.getField("ORDER-NOTE"));
                System.out.println("]");
            }
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     *Can the structure OCCURS be a record format definition? => I can't.
     *
     * @throws XmlException Record format exception
     * @throws IOException File IO Exception
     */
    @Test
    public void test09() throws XmlException, IOException {
        //AS400 data type definition
        AS400DataType[] as400Types = new AS400DataType[] {
                new AS400Text(10),
                new AS400PackedDecimal(4, 2),
        };
        AS400Structure as400Structure = new AS400Structure(as400Types);
        AS400Array as400Array = new AS400Array(as400Structure, 3);

        //Java object => AS400 data byte array
        Object[] inJavaObject = new Object[as400Array.getNumberOfElements()];
        System.out.println("test09:AS400Array elements:" + as400Array.getNumberOfElements());
        for (int i = 0; i < inJavaObject.length; i++) {
            inJavaObject[i] = new Object[] {
                    new String("170415-123"),
                    new BigDecimal("-12.34"),
            };
        }
        byte[] as400bytes = as400Array.toBytes(inJavaObject);
        StringUtil.dump("test09:", as400bytes, "");

        //AS400 data type(Structure+Array)Can record format definitions be saved in XML? => I can't.
        ArrayFieldDescription afd = new ArrayFieldDescription(as400Array, "AMOUNTS");
        RecordFormat rf = new RecordFormat("TEST09-REC");
        rf.addFieldDescription(afd);
        File rfml = new File("src/test/java/jp/golden/cat/util/TEST09.rfml");
        FileOutputStream fos = null;
        try {
            //In RecordFormat, AS400Structure cannot be saved as an RFML file with the following exception.
            // com.ibm.as400.data.XmlException: Data type AS400Structure is not
            // supported by RFML.
            //The API docs also said it wasn't supported. I also want to see the multidimensional AS400Array.
            // https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzahh/javadoc/com/ibm/as400/data/RecordFormatDocument.html
            thrown.expect(com.ibm.as400.data.XmlException.class);
            thrown.expectMessage("Data type AS400Structure is not supported by RFML.");
            RecordFormatDocument rfd = new RecordFormatDocument(rf);
            fos = new FileOutputStream(rfml);
            rfd.toXml(fos);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }

    /**
     * AS400Array+AS400Structure cannot be made into an RFML file, so keep it until the number of bytes is counted and save it in the RFML file.
     *
     */
    @Test
    public void test10() {
        //AS400 data type definition
        AS400DataType[] types = new AS400DataType[] {
                new AS400Text(10),
                new AS400PackedDecimal(4, 2),
        };
        AS400Structure a400Structure = new AS400Structure(types);
        AS400Array as400Array = new AS400Array(a400Structure, 3);
        System.out.println("test10:AS400Structure byte length:" + a400Structure.getByteLength());
        System.out.println("test10:AS400Arrat byte length:" + as400Array.getByteLength());
        // AS400Array+Create a definition of AS400ByteArray for the number of bytes of AS400Structure.
        AS400ByteArray as400ByteArray = new AS400ByteArray(as400Array.getByteLength());

        //Create a record format definition.
        BinaryFieldDescription bfd = new BinaryFieldDescription(new AS400Bin4(), "REC-NO");
        CharacterFieldDescription cfd1 = new CharacterFieldDescription(new AS400Text(10), "ORDER-NO");
        HexFieldDescription hfd = new HexFieldDescription(as400ByteArray, "AMOUNTS");   // AS400Array+AS400ByteArray for the number of bytes of AS400Structure
        CharacterFieldDescription cfd2 = new CharacterFieldDescription(new AS400Text(20), "ORDER-NOTE");
        RecordFormat rf = new RecordFormat("ORDER3-REC");
        rf.addFieldDescription(bfd);
        rf.addFieldDescription(cfd1);
        rf.addFieldDescription(hfd);
        rf.addFieldDescription(cfd2);

        //Save to RFML file.
        File rfml = new File("src/test/java/jp/golden/cat/util/ORDER3.rfml");
        this.saveRecordFormat(rf, rfml);
    }

    /**
     * AS400Array+Since AS400Structure cannot be made into an RFML file, the record is output as an RFML file that expresses that part in a byte array..
     *
     * @throws XmlException Record format exception
     * @throws IOException File IO Exception
     */
    @Test
    public void test11() throws XmlException, IOException {
        //AS400 data type definition
        AS400DataType[] types = new AS400DataType[] {
                new AS400Text(10),
                new AS400PackedDecimal(4, 2),
        };
        AS400Structure as400structure = new AS400Structure(types);
        AS400Array as400array = new AS400Array(as400structure, 3);

        //Generate a record object from an RFML file.
        RecordFormatDocument rfd = this.loadRecordFormatDocument("jp.golden.cat.util.ORDER3.rfml");
        Record rec = rfd.toRecordFormat("ORDER3-REC").getNewRecord();

        //MF file output
        File ioFile = new File("./data/AS4003.dat");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(ioFile);
            //10 record generation
            for (int i = 1; i < 11; i++) {
                rec.setField("REC-NO", new Integer(i));
                rec.setField("ORDER-NO", "170415-123");

                // AS400Array+Create a byte array for the AS400 Structure part
                //First, create a Java object to set
                Object[] inJavaObject = new Object[as400array.getNumberOfElements()];
                for (int j = 0; j < inJavaObject.length; j++) {
                    inJavaObject[j] = new Object[] {
                            new String("170415-AA" + j),
                            new BigDecimal("-12.34"),
                    };
                }
                //Image of creating a byte array from a 2D Java object
                byte[] as400bytes = as400array.toBytes(inJavaObject);
                System.out.println("test11:" + as400bytes.length);
                //Set that byte array
                rec.setField("AMOUNTS", as400bytes);

                rec.setField("ORDER-NOTE", "HELLOαWORLD");
                fos.write(rec.getContents());
            }
            fos.flush();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * AS400Array+AS400Structure cannot be converted into an RFML file, so consider AS400Structure as one record, convert it to an RFML file, and read it.
     *
     * @throws XmlException Record format exception
     * @throws IOException File IO Exception
     */
    @Test
    public void test12() throws IOException, XmlException {
        //Generate a record object from an RFML file.
        RecordFormatDocument rfd = this.loadRecordFormatDocument("jp.golden.cat.util.ORDER4.rfml");
        Record rec1 = rfd.toRecordFormat("ORDER4-REC").getNewRecord();
        //Record definition for 1 OCCURS. I really want to use AS400 Structure, but it's not supported.
        Record rec2 = rfd.toRecordFormat("AMOUNTS").getNewRecord();

        //MF file input
        File ioFile = new File("./data/AS4003.dat");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(ioFile);
            //Get the number of bytes for one record and generate a byte array.
            int len = rec1.getRecordLength();
            byte[] buffer = new byte[len];
            //There are no newline characters in the MF file. Therefore, the number of bytes in one record is read into the buffer.
            while (fis.read(buffer) != -1) {
                rec1.setContents(buffer);

                System.out.print("[" + rec1.getField("REC-NO"));
                System.out.print("],[" + rec1.getField("ORDER-NO"));

                //Get the entire OCCURS byte array
                byte[] amounts = (byte[]) rec1.getField("AMOUNTS");
                //Byte length of the entire OCCURS ÷ 1 Byte length for OCCURS = Number of OCCCURS
                int occursCount = amounts.length / rec2.getRecordLength();
                //OCCURS Repeat for a few minutes
                for (int i = 0; i < occursCount; i++) {
                    //Crop 1 OCCURS from the amounts byte array.
                    rec2.setContents(amounts, i * rec2.getRecordLength());
                    //1 ORDER that makes up OCCURS-Standard output of CNO and AMOUNT
                    System.out.print("],[" + rec2.getField("ORDER-CNO"));
                    System.out.print("],[" + rec2.getField("AMOUNT"));
                }
                System.out.print("],[" + rec1.getField("ORDER-NOTE"));
                System.out.println("]");
            }
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     *Behavior test of AS400Array.
     *
     *The breakdown of AS400Array is a total of 30 bytes of data structure of X type 10 bytes x 3 OCCURS.
     *Save this in an RFML file. Count in XML="3"Expected to be output with. So far, it worked.
     *However, when I read the RFML file, create a Record object, and get the length, it is 10 bytes. OCCURS is not reproduced.
     *
     *Conclusion:It is possible to write count to an RFML file, but do not use count in an RFML file as it will be lost when read.
     *
     * @throws XmlException Record format exception
     * @throws IOException File IO Exception
     */
    @Test
    public void test13() throws IOException, XmlException {
        //Item definition
        AS400Array as400Array = new AS400Array(new AS400Text(10), 3);
        ArrayFieldDescription afd = new ArrayFieldDescription(as400Array, "ORDER-NOTE");

        //Save the record format definition as a file.
        RecordFormat rf = new RecordFormat("TEST13-REC");
        rf.addFieldDescription(afd);
        //Expected value:10*3=30byte.
        int expected = rf.getNewRecord().getRecordLength();
        // count="3"It is saved in.
        this.saveRecordFormat(rf, new File("src/test/java/jp/golden/cat/util/TEST13.rfml"));

        //Generate a record object from an RFML file.
        RecordFormatDocument rfd = this.loadRecordFormatDocument("jp.golden.cat.util.TEST13.rfml");
        // 10*3=I'm expecting 30 bytes, but for some reason 10 is returned. count is ignored.
        int actual = rfd.toRecordFormat("TEST13-REC").getNewRecord().getRecordLength();
        //When I try to save it as after, the count disappears and it is saved.
        this.saveRecordFormat(rfd.toRecordFormat("TEST13-REC"),
                new File("src/test/java/jp/golden/cat/util/TEST13_after.rfml"));

        System.out.println("test13:actual:" + actual);
        System.out.println("test13:expected:" + expected);
        // assertEquals(expected, actual);
        assertEquals(10, actual);

    }

    /**
     *Create order record format definition.
     *
     * @return Create order record format definition.
     */
    RecordFormat createOrderRecordFormat() {
        //Item definition
        BinaryFieldDescription bfd = new BinaryFieldDescription(new AS400Bin4(), "REC-NO");
        CharacterFieldDescription cfd1 = new CharacterFieldDescription(new AS400Text(10), "ORDER-NO");
        PackedDecimalFieldDescription pfd = new PackedDecimalFieldDescription(new AS400PackedDecimal(4, 2), "AMOUNT");
        CharacterFieldDescription cfd2 = new CharacterFieldDescription(new AS400Text(20), "ORDER-NOTE");

        //Record format definition
        RecordFormat rf = new RecordFormat("ORDER-REC");
        rf.addFieldDescription(bfd);
        rf.addFieldDescription(cfd1);
        rf.addFieldDescription(pfd);
        rf.addFieldDescription(cfd2);

        return rf;
    }

    /**
     *Gets the MF record format definition from the classpath.
     *
     * @param rfmlClassPath。
     * @return MF record format definition.
     */
    RecordFormatDocument loadRecordFormatDocument(String rfmlClassPath) {
        RecordFormatDocument rfd = null;
        try {
            rfd = new RecordFormatDocument(rfmlClassPath);
        } catch (XmlException e) {
            throw new RuntimeException(e);
        }
        return rfd;
    }

    /**
     *Save the MF record format definition as a file.
     *
     * @param rf MF record definition format.
     * @param rfml
     *MF record format definition file path. Note that if there is no definition file on the classpath, the definition file cannot be read later.
     */
    void saveRecordFormat(RecordFormat rf, File rfml) {
        FileOutputStream fos = null;
        try {
            RecordFormatDocument rfd = new RecordFormatDocument(rf);
            fos = new FileOutputStream(rfml);
            rfd.toXml(fos);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (XmlException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     *Endian confirmation.
     *
     *Native byte order:LITTLE_ENDIAN<br>
     *initial value:0A0B0C0D12345678<br>
     * LITTLE_ENDIAN:0D0C0B0A BIG_ENDIAN:0A0B0C0D<br>
     * LITTLE_ENDIAN:78563412 BIG_ENDIAN:12345678<br>
     */
    @Test
    public void testEndian() {
        System.out.println("testEndian:");
        //Standard output of native byte order.
        //In Java internal processing, it is big endian, but Intel CPU is little endian, so if you run the following method on Java VM of Intel CPU,
        //"Native byte order:LITTLE_"ENDIAN" is displayed.
        System.out.println("Native byte order:" + ByteOrder.nativeOrder());

        byte[] mfData = new byte[] { 0xA, 0xB, 0xC, 0xD, 0x12, 0x34, 0x56, 0x78 };
        StringUtil.dump("initial value:", mfData, "");

        ByteArrayInputStream bis = null;
        try {
            bis = new ByteArrayInputStream(mfData);

            byte[] buffer = new byte[4];
            int len = -1;
            while ((len = bis.read(buffer)) != -1) {
                if (len != buffer.length) {
                    throw new IOException("Review the relationship between the length of the read buffer and the input value.");
                }
                //Converted with little endian.
                ByteBuffer byteBuff = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
                int i = byteBuff.getInt();
                System.out.print(ByteOrder.LITTLE_ENDIAN);
                System.out.print(":" + StringUtil.toHexString(BigInteger.valueOf(i).toByteArray()));
                System.out.print("\t");

                //Converted with big endian.
                byteBuff = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN);
                i = byteBuff.getInt();
                System.out.print(ByteOrder.BIG_ENDIAN);
                System.out.print(":" + StringUtil.toHexString(BigInteger.valueOf(i).toByteArray()));
                System.out.println();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    ;
                }
            }
        }
    }
}

Recommended Posts

How to input / output IBM mainframe files in Java?
[Java] How to output and write files!
[Java] How to get and output standard input
How to disassemble Java class files
How to learn JAVA in 7 days
Log output to file in Java
How to decompile java class files
How to use classes in Java?
How to name variables in Java
How to concatenate strings in java
How to implement date calculation in Java
How to implement Kalman filter in Java
Multilingual Locale in Java How to use Locale
How to do base conversion in Java
How to output Java string to console screen
Convert SVG files to PNG files in Java
How to implement coding conventions in Java
How to embed Janus Graph in Java
How to get the date in java
[Java] How to receive numerical data separated by spaces in standard input
(Memo) How to solve dummy output in Ubuntu 20.04
How to color code console output in Eclipse
How to display a web page in Java
How to get Class from Element in Java
How to update pre-built files in docker container
How to hide null fields in response in Java
I tried to output multiplication table in Java
[Java] How to substitute Model Mapper in Jackson
How to solve an Expression Problem in Java
How to write Java String # getBytes in Kotlin?
How to call functions in bulk with Java reflection
[Ruby] How to use standard output in conditional branching
How to create a data URI (base64) in Java
Android-Upload image files to Azure Blob Storage in Java
How to generate / verify ID token in Java Memo
How to convert A to a and a to A using AND and OR in Java
How to handle TSV files and CSV files in Ruby
How to convert a file to a byte array in Java
How to Git manage Java EE projects in Eclipse
How to Syntax Highlight files like Dockerfile.production in Pycharm
Summary of how to implement default arguments in Java
Gzip-compress byte array in Java and output to file
How to put old Java (8 series) in macOS 10.15 Catalina
Notes on how to use regular expressions in Java
[Java] How to use Map
Read binary files in Java 1
[Java] How to use Map
How to uninstall Java 8 (Mac)
Java --How to make JTable
How to use java Optional
How to minimize Java images
How to write java comments
How to use java class
[Java] How to use Optional ②
Read standard input in Java
[Java] How to use removeAll ()
[Java] How to display Wingdings
[Java] How to use string.format
How to use Java Map
How to set Java constants
Read binary files in Java 2