import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import dto.Kyu04Rec; import dto.Kyu05Rec; import dto.Kyu99Rec; import dto.ZandatacPar; import dto.ZanendacPar; import dto.ZanmsgacPar; import util.ConvUtil; /** * ============================================================ * 【COBOL→Java】KYU08PRI 給与明細印刷処理 * ============================================================ * PROGRAM-ID. KYU08PRI → 类 Kyu08PriMain(PGMパターン:レイアウト編集のみ GETPUT) * * 使用ファイル(SELECT ASSIGN → パス定数): * R01INNFIL(KYU08R01 SALARY-NET F200B 入力) → R01INN_FILE(COPY KYU04REC → dto.Kyu04Rec) * W01OUTFIL(KYU08W01 PAYSLIP F200B 出力) → W01OUT_FILE(COPY KYU05REC → dto.Kyu05Rec) * W02OUTFIL(KYU08W02 ERROR-LOG VB200B 出力)→ W02OUT_FILE(COPY KYU99REC → dto.Kyu99Rec、将来拡張・現状出力なし) * * 使用SUB(CALL → 类#execute): * SUB01DAT→Sub01DatSub / SUB02MSG→Sub02MsgSub / SUB03END→Sub03EndSub * * 段落対応: * 0000MAJCOLSOR→run() 1000ITTSOR→initPara() 1100R01INNSOR→readR01Para() * 2000MAJSOR→majPara() 3000STPSOR→termPara() 4000MSGOUTSOR→msgOutPara() * 9999ABDSOR→abendPara() * * 処理要点(詳細設計書_KYU08PRI 準拠): * - 1社員1ページ。ヘッダ行→明細行→フッタ行を 200B 固定長で順次出力(LINAGE 66行/ページ相当)。 * - ヘッダ行:HL-TITLE('========== 給与明細書 ==========') + ページ番号(Z(03)9) + 'PG'。 * - 明細行:DETAIL-LINE は KYU05REC 布局と一致(PAGE-TITLE=SPACE)するため Kyu05Rec で編集。 * EMP-NAME は X(020) JUSTIFIED RIGHT、BASIC/NET は Z(008)9 BLANK WHEN ZERO で編集。 * - フッタ行:FL-MSG('--- 明細終了 ---')。 * - COBOL は LINAGE/ADVANCING PAGE を用いるが、Java 平文テキスト出力では 200B 行の連続書出とし * ページ制御(フォームフィード等)は行わない(設計上 1社員1ページのため実害なし)。 * - W02(ERROR-LOG) は OPEN OUTPUT のみ(現状 WRITE なし/将来拡張)。 */ public class Kyu08PriMain { // ==================== コンスタント領域 CNSARA ==================== /** 03 CNS-PRGIDX PIC X(008) VALUE 'KYU08PRI'. */ private static final String CST_PRGIDX = "KYU08PRI"; /** 03 CNS-MSGSTR PIC 9(003) VALUE 001. 開始メッセージ */ private static final int CST_MSGSTR = 1; /** 03 CNS-MSGFIN PIC 9(003) VALUE 002. 終了メッセージ */ private static final int CST_MSGFIN = 2; /** 03 CNS-MSGSUBEEK PIC 9(003) VALUE 005. サブエラーメッセージ */ private static final int CST_MSGSUBEEK = 5; /** 03 CNS-MSGIINKES PIC 9(003) VALUE 006. 入力件数メッセージ */ private static final int CST_MSGIINKES = 6; /** 03 CNS-MSGOUTKES PIC 9(003) VALUE 007. 出力件数メッセージ */ private static final int CST_MSGOUTKES = 7; /** 03 CNS-MSGKEYINF PIC 9(003) VALUE 033. コンパイル日時メッセージ */ private static final int CST_MSGKEYINF = 33; /** 03 CNS-KN0002 PIC 9(001) VALUE 2. */ private static final int CST_KN0002 = 2; /** 03 CNS-ABD999 PIC 9(003) VALUE 999. */ private static final int CST_ABD999 = 999; /** 03 CNS-HEADER-LINES PIC 9(002) VALUE 6. (LINAGE TOP 相当・参照用) */ private static final int CST_HEADER_LINES = 6; /** 03 CNS-DETAIL-LINES PIC 9(002) VALUE 54. (LINAGE 明細相当・参照用) */ private static final int CST_DETAIL_LINES = 54; /** 03 CNS-FOOTER-LINES PIC 9(002) VALUE 6. (LINAGE BOTTOM 相当・参照用) */ private static final int CST_FOOTER_LINES = 6; // ==================== ファイル定義(SELECT ASSIGN 相当) ==================== /** SELECT R01INNFIL ASSIGN TO KYU08R01.(SALARY-NET 入力 F200B) */ private static final String R01INN_FILE = "data/KYU08R01.txt"; /** SELECT W01OUTFIL ASSIGN TO KYU08W01.(PAYSLIP 出力 F200B) */ private static final String W01OUT_FILE = "data/KYU08W01.txt"; /** SELECT W02OUTFIL ASSIGN TO KYU08W02.(ERROR-LOG 出力 VB200B・現状出力なし) */ private static final String W02OUT_FILE = "data/KYU08W02.txt"; // ==================== カウンタ領域 CUNARA(COMP-3 → long) ==================== /** 03 CUN-R01INN PIC S9(009) COMP-3 VALUE ZERO. */ private long cunR01Inn; /** 03 CUN-W01OUT PIC S9(009) COMP-3 VALUE ZERO. */ private long cunW01Out; /** 03 CUN-W02OUT PIC S9(009) COMP-3 VALUE ZERO. */ private long cunW02Out; // ==================== 作業領域 WRKARA ==================== /** 03 WRK-EOF PIC X(001). 読込フラグ(88 WRK-EOF-Y VALUE '1') */ private String wrkEof = "0"; /** 03 WRK-LINE-NO PIC 9(002). */ private int wrkLineNo; /** 03 WRK-PAGE-COUNTER PIC 9(004) VALUE 0. */ private int wrkPageCounter = 0; /** 03 WS-PAD-CHAR PIC X(001) VALUE SPACE. */ private String wsPadChar = " "; /** 03 WS-FILE-STATUS PIC X(002). */ private String wsFileStatus = ""; /** 03 WS-DISP-PG PIC Z(03)9. (HL-PAGE-NO 編集用・参照用) */ private int wsDispPg; /** 88 WRK-EOF-Y VALUE '1'. */ private boolean isEof() { return "1".equals(wrkEof); } // ==================== サブプログラム連絡領域(LINKAGE COPY → DTO) ==================== /** COPY ZANMSGAC.(SUB02MSG用) */ private final ZanmsgacPar m00MhoPar = new ZanmsgacPar(); /** COPY ZANENDAC.(SUB03END用) */ private final ZanendacPar e01AbdPar = new ZanendacPar(); /** COPY ZANDATAC.(SUB01DAT用) */ private final ZandatacPar d01UbsPar = new ZandatacPar(); // ==================== 入出力レコード(FILE SECTION → DTO) ==================== /** 01 R01INNREC.(FD R01INNFIL / COPY KYU04REC) */ private final Kyu04Rec r01 = new Kyu04Rec(); /** 01 W01OUTREC.(FD W01OUTFIL / COPY KYU05REC) */ private final Kyu05Rec w01 = new Kyu05Rec(); /** 01 W02OUTREC.(FD W02OUTFIL / COPY KYU99REC) */ private final Kyu99Rec w02 = new Kyu99Rec(); // ==================== ファイルハンドル(OPEN/CLOSE 相当) ==================== private BufferedReader r01InnFil; private PrintWriter w01OutFil; private PrintWriter w02OutFil; // ==================== PROCEDURE DIVISION ==================== public static void main(final String[] args) { new Kyu08PriMain().run(); } /** * 段落 0000MAJCOLSOR:制御処理。 * PERFORM 1000ITTSOR → PERFORM 2000MAJSOR UNTIL WRK-EOF-Y → PERFORM 3000STPSOR → GOBACK. */ private void run() { try { initPara(); while (!isEof()) { // PERFORM 2000MAJSOR UNTIL WRK-EOF = '1' majPara(); } termPara(); } finally { closeFiles(); } } /** * 段落 1000ITTSOR:初期処理。 * 開始メッセージ→コンパイル日時→ワーク初期化→SUB01DAT→OPEN→初回読込。 */ private void initPara() { // *** 開始メッセージ出力 // MOVE CNS-MSGSTR TO M00MSGCOD./PERFORM 4000MSGOUTSOR. msgOutPara(CST_MSGSTR, null, null); // *** コンパイル日時出力 ※FUNCTION WHEN-COMPILED→起動時刻で代用 // MOVE CNS-MSGKEYINF TO M00MSGCOD. // MOVE FUNCTION WHEN-COMPILED TO M00UMKDATS22-01. // MOVE 'COMPILED' TO M00UMKDATS22-02. final String compiled = java.time.LocalDateTime.now() .format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); msgOutPara(CST_MSGKEYINF, compiled, "COMPILED"); // *** ワークエリア初期化 INITIALIZE WRKARA. wrkEof = "0"; wrkLineNo = 0; wrkPageCounter = 0; wsPadChar = " "; wsFileStatus = ""; wsDispPg = 0; // *** 運用日付取得 SUB01DAT // INITIALIZE D01UBSPAR./CALL 'SUB01DAT' USING D01UBSPAR. d01UbsPar.reset(); Sub01DatSub.execute(d01UbsPar); if (d01UbsPar.getD01FkiCod() != 0) { // IF D01FKICOD NOT = ZERO // MOVE CNS-MSGSUBEEK TO M00MSGCOD // MOVE 'SUB01DAT' TO M00UMKDATS22-01 // MOVE D01FKICOD TO M00UMKDATS22-02 // PERFORM 4000MSGOUTSOR // PERFORM 9999ABDSOR msgOutPara(CST_MSGSUBEEK, "SUB01DAT", ConvUtil.padLeftZero(d01UbsPar.getD01FkiCod(), 4)); abendPara(); return; } // *** 使用ファイルのオープン // OPEN INPUT R01INNFIL./OPEN OUTPUT W01OUTFIL W02OUTFIL. openFiles(); // *** R01 初回読込(1100R01INNSOR) readR01Para(); } /** * 段落 1100R01INNSOR:R01読込処理。 * READ R01INNFIL AT END SET WRK-EOF-Y / NOT AT END ADD 1 TO CUN-R01INN. */ private void readR01Para() { try { final String line = r01InnFil.readLine(); if (line == null) { // AT END: SET WRK-EOF-Y TO TRUE. wrkEof = "1"; } else { // NOT AT END: 固定長解析 → R01INNREC、ADD 1 TO CUN-R01INN. r01.parseRecord(line); cunR01Inn++; } } catch (IOException e) { throw new UncheckedIOException(e); // READ異常 ≈ ABEND } } /** * 段落 2000MAJSOR:主処理。 * ヘッダ出力(改ページ+ページ番号)→明細行編集・出力→フッタ出力(改ページ)→次レコード読込。 */ private void majPara() { // *** ページカウンタ加算 // ADD 1 TO WRK-PAGE-COUNTER. wrkPageCounter++; // MOVE WRK-PAGE-COUNTER TO HL-PAGE-NO./MOVE WRK-PAGE-COUNTER TO WS-DISP-PG. wsDispPg = wrkPageCounter; // *** ヘッダ出力(BEFORE ADVANCING PAGE) // WRITE W01OUTREC FROM HEADER-LINE BEFORE ADVANCING PAGE. w01OutFil.println(buildHeaderLine(wrkPageCounter)); // *** 明細行編集(DETAIL-LINE ≡ KYU05REC 布局:PAGE-TITLE=SPACE) // MOVE R01EMP-ID TO DL-EMP-ID. // MOVE R01EMP-NAME TO DL-EMP-NAME. // MOVE R01GROSS-PAYMENT TO DL-BASIC-SALARY. // MOVE R01NET-PAYMENT TO DL-NET-PAYMENT. w01.reset(); w01.setW01EmpId(r01.getR01EmpId()); w01.setW01EmpName(r01.getR01EmpName()); w01.setW01BasicSalary((int) r01.getR01GrossPayment()); w01.setW01NetPayment((int) r01.getR01NetPayment()); w01.setW01PageTitle(""); // 明細行の PAGE-TITLE は SPACE // WRITE W01OUTREC FROM DETAIL-LINE BEFORE ADVANCING 1 LINE // AT END-OF-PAGE WRITE W01OUTREC FROM FOOTER-LINE BEFORE ADVANCING PAGE. // (1社員1ページのため AT END-OF-PAGE は発動しない) w01OutFil.println(w01.toRecordLine()); // ADD 1 TO CUN-W01OUT. cunW01Out++; // *** フッタ出力(BEFORE ADVANCING PAGE) // WRITE W01OUTREC FROM FOOTER-LINE BEFORE ADVANCING PAGE. w01OutFil.println(buildFooterLine()); // *** 次レコード読込(1100R01INNSOR) readR01Para(); } /** * 段落 3000STPSOR:終了処理。 * CLOSE→入出力件数メッセージ×3→終了メッセージ。 */ private void termPara() { // *** 入出力ファイルのクローズ // CLOSE R01INNFIL W01OUTFIL W02OUTFIL. closeFiles(); // *** 入出力件数メッセージ出力 // MOVE CNS-MSGIINKES TO M00MSGCOD. MOVE 'KYU08R01' TO PARM1. MOVE CUN-R01INN TO PARM2. msgOutPara(CST_MSGIINKES, "KYU08R01", ConvUtil.padLeftZero(cunR01Inn, 9)); // MOVE CNS-MSGOUTKES TO M00MSGCOD. MOVE 'KYU08W01' TO PARM1. MOVE CUN-W01OUT TO PARM2. msgOutPara(CST_MSGOUTKES, "KYU08W01", ConvUtil.padLeftZero(cunW01Out, 9)); // MOVE CNS-MSGOUTKES TO M00MSGCOD. MOVE 'KYU08W02' TO PARM1. MOVE CUN-W02OUT TO PARM2. msgOutPara(CST_MSGOUTKES, "KYU08W02", ConvUtil.padLeftZero(cunW02Out, 9)); // *** 終了メッセージ出力 // MOVE CNS-MSGFIN TO M00MSGCOD./PERFORM 4000MSGOUTSOR. msgOutPara(CST_MSGFIN, null, null); } /** * 段落 4000MSGOUTSOR:メッセージ編集出力処理。 * M00UMKDATS22-03(1:1)/-04(1:1) に '2'、-05 にプログラムIDを設定し SUB02MSG を呼ぶ。 */ private void msgOutPara(final int msgCod, final String p1, final String p2) { m00MhoPar.reset(); m00MhoPar.setM00MsgCod(msgCod); if (p1 != null) { m00MhoPar.setM00Umkdats2201(p1); } if (p2 != null) { m00MhoPar.setM00Umkdats2202(p2); } // MOVE CNS-KN0002 TO M00UMKDATS22-03(1:1). → 先頭1桁 '2' + 後続SPACE(30) m00MhoPar.setM00Umkdats2203(ConvUtil.padRight(Integer.toString(CST_KN0002), 30)); // MOVE CNS-KN0002 TO M00UMKDATS22-04(1:1). m00MhoPar.setM00Umkdats2204(ConvUtil.padRight(Integer.toString(CST_KN0002), 30)); // MOVE CNS-PRGIDX TO M00UMKDATS22-05. m00MhoPar.setM00Umkdats2205(CST_PRGIDX); Sub02MsgSub.execute(m00MhoPar); } /** * 段落 9999ABDSOR:ABEND処理。 * MOVE 999 TO E01ABDCOD./CALL 'SUB03END' USING E01ABDPAR. */ private void abendPara() { e01AbdPar.reset(); e01AbdPar.setE01AbdCod(CST_ABD999); Sub03EndSub.execute(e01AbdPar); } // ==================== 印刷行編集補助(HEADER-LINE / FOOTER-LINE) ==================== /** * 01 HEADER-LINE 編集(200B)。 * 05 HL-TITLE PIC X(040) VALUE '========== 給与明細書 ==========' * 05 FILLER PIC X(010) VALUE SPACES * 05 HL-PAGE-NO PIC Z(03)9 * 05 FILLER PIC X(002) VALUE 'PG' * 05 FILLER PIC X(144) VALUE SPACES */ private static String buildHeaderLine(final int pageNo) { // PIC Z(03)9 編集(4桁、先頭3桁ゼロ抑制) final String pageNoEd = editZ(pageNo, 3); return ConvUtil.padRight("========== 給与明細書 ==========", 40) + ConvUtil.padRight("", 10) + pageNoEd + "PG" + ConvUtil.padRight("", 144); } /** * 01 FOOTER-LINE 編集(200B)。 * 05 FL-MSG PIC X(040) VALUE '--- 明細終了 ---' * 05 FILLER PIC X(160) VALUE SPACES */ private static String buildFooterLine() { return ConvUtil.padRight("--- 明細終了 ---", 40) + ConvUtil.padRight("", 160); } /** PIC Z(n)9 BLANK WHEN ZERO 編集(編集桁数 = zCount + 1)。 */ private static String editZ(final int val, final int zCount) { final int len = zCount + 1; if (val == 0) { // BLANK WHEN ZERO return ConvUtil.padRight("", len); } final String s = ConvUtil.padLeftZero(val, len); final char[] ca = s.toCharArray(); for (int i = 0; i < zCount; i++) { if (ca[i] == '0') { ca[i] = ' '; } else { break; } } return new String(ca); } // ==================== ファイル操作補助(OPEN/CLOSE 相当) ==================== /** OPEN INPUT R01INNFIL / OUTPUT W01OUTFIL W02OUTFIL. */ private void openFiles() { try { r01InnFil = new BufferedReader( new InputStreamReader(new FileInputStream(R01INN_FILE), StandardCharsets.UTF_8)); w01OutFil = new PrintWriter( new OutputStreamWriter(new FileOutputStream(W01OUT_FILE), StandardCharsets.UTF_8)); w02OutFil = new PrintWriter( new OutputStreamWriter(new FileOutputStream(W02OUT_FILE), StandardCharsets.UTF_8)); } catch (IOException e) { throw new UncheckedIOException(e); // OPEN失敗 ≈ ABEND } } /** CLOSE R01INNFIL W01OUTFIL W02OUTFIL. */ private void closeFiles() { closeQuiet(r01InnFil); closeQuiet(w01OutFil); closeQuiet(w02OutFil); r01InnFil = null; w01OutFil = null; w02OutFil = null; } private static void closeQuiet(final Object handle) { try { if (handle instanceof BufferedReader) { ((BufferedReader) handle).close(); } else if (handle instanceof PrintWriter) { ((PrintWriter) handle).close(); } } catch (IOException e) { // CLOSE異常は無視(原程序もFILE STATUS未管理) } } }