/* * 著作権表記 TODO 要否をお客様に確認 */ package com.pgf.mqspring.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; import java.util.List; import javax.xml.bind.DatatypeConverter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.pgf.mqspring.TelegramInfo; import com.pgf.mqspring.model.ReceiveMapVectorRequestModel; import com.pgf.mqspring.model.TelegramInfoModel; /** * コード変換サービス実装クラス *

* アプリケーション内のコード変換共通処理を提供する。 * */ public class CharacterCodeConverter { /** シフトイン区切り文字 */ private static final byte SI = 14; /** シフトアウト区切り文字 */ private static final byte SO = 15; /** エンコーディング EBCDIC */ private static final String EBCDIC = "Cp930"; /** フィールド長 */ private static final int ATTRIBUTE_LENGTH = 5; /** フィールド属性初期値 */ private static final byte DEFAULT_FORMAT[] = { -52 }; /** カラー属性初期値 */ private static final byte DEFAULT_COLOR[] = new byte[1]; /** 強調属性初期値 */ private static final byte DEFAULT_TURN[] = new byte[1]; private static final String DFHPF8 = "DFHPF8"; private static final String DFHPF10 = "DFHPF10"; private static final String DFHENTER = "DFHENTER"; /** 特殊電文強調区分 */ private static final byte RMVAID_DFHENTER[] = { 125, 64, 64, 64 }; private static final byte RMVAID_DFHPF8[] = {-8, 64, 64, 64}; private static final byte RMVAID_DFHPF10[] = {122, 64, 64, 64}; private static HashMap> TelegramInfoList = TelegramInfo.getTelegramInfoList(); /** エラーカラー属性マップ */ public static final HashMap ERR_COLOR_MAP = new HashMap(); /** ロガー */ private static final Logger logger = LogManager.getLogger(); private CharacterCodeConverter() { } /** * 16進文字列符号化処理 *

* 符号化対象文字列がnullまたは空文字の場合
*  nullを返却する。
* 符号化対象文字列を先頭から2文字ずつ分割する。
* 分割した件数分、以下の処理を繰り返す。
*  分割結果を16進数とし、数値に変換する。
*  変換した数値をバイト配列に追加する
* バイト配列を返却する * * @param data 符号化対象文字列 * @return dataを符号化したバイト配列 */ // public static byte[] encodeHexStringData(String data) { // if (data == null || data.length() <= 0) // return null; // String tmp = null; // int telLen = data.length(); // byte res[] = new byte[telLen / 2]; // for (int i = 0; i < telLen; i += 2) { // tmp = data.substring(i, i + 2); // res[i / 2] = (byte) Integer.parseInt(tmp, 16); // } // return res; // } /** * Vectorデータ符号化処理 *

* {@link #encodeHexStringData(String) encodeHexStringData}を実行し、ベクターデータ長を取得する。以下引数
*  ・Vectorデータ.vectorLength
* {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、ベクター標識を取得する。以下引数
*  ・Vectorデータ.vectorDescriptor
*  ・4
*  ・false
* {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、ベクタータイプを取得する。以下引数
*  ・Vectorデータ.vectorType
*  ・1
*  ・false
* {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、ベクターバージョンを取得する。以下引数
*  ・Vectorデータ.vectorVersion(7桁左空白パディング)
*  ・7
*  ・false
* {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、SEND域保存フラグを取得する。以下引数
*  ・Vectorデータ.rmTransmitSendAreas
*  ・4
*  ・false
* {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、MAPSET名を取得する。以下引数
*  ・Vectorデータ.rmMapset
*  ・8
*  ・false
* {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、MAP名を取得する。以下引数
*  ・Vectorデータ.rmMap
*  ・8
*  ・false
* 特殊電文要求区分に{ 125, 64, 64, 64 }を設定する
* カーソルオフセットにデータ長4の空のバイト配列を設定する
* Vectorデータ.rmCposnの文字列長が1以上の場合 * {@link #encodeStringData(String, int, boolean) encodeStringData}を実行し、カーソルオフセットを取得する。以下引数
*   ・Vectorデータ.rmCposn
*   ・4
*   ・false
* {@link #encodeHexStringData(String) encodeHexStringData}を実行し、データ長を取得する。以下引数
*  ・Vectorデータ.rmDataLen
* 以下のデータを連結し、Vectorデータ符号化バイト配列とする
*  ・ベクターデータ長
*  ・ベクター標識
*  ・ベクタータイプ
*  ・ベクターバージョン
*  ・SEND域保存フラグ
*  ・MAPSET名
*  ・MAP名
*  ・特殊電文要求区分
*  ・カーソルオフセット
*  ・データ長
* Vectorデータ符号化バイト配列を返却する。 * * @param model Vectorデータ * @param length 指定長 * @return Vectorデータを符号化したバイト配列 */ public static byte[] encodeVectorData(String dispCode, ReceiveMapVectorRequestModel model, int length) { byte[] vectorDescriptor = encodeStringData(model.getVectorDescriptor(), 4, false); byte[] vectorType = encodeStringData(model.getVectorType(), 1, false); byte[] vectorVersion = encodeStringData(String.format("%7s", model.getVectorVersion()), 7, false); byte[] rmTsa = encodeStringData(model.getRmTransmitSendAreas(), 4, false); byte[] rmMapset = encodeStringData(model.getRmMapset(), 8, false); byte[] rmMap = encodeStringData(model.getRmMap(), 8, false); byte[] rmAid = RMVAID_DFHENTER; if(model.getRmAid().equals(DFHPF8)) rmAid = RMVAID_DFHPF8; else if(model.getRmAid().equals(DFHPF10)) rmAid = RMVAID_DFHPF10; else if(model.getRmAid().equals(DFHENTER)) rmAid = RMVAID_DFHENTER; byte[] rmCposn = new byte[4]; if (!model.getRmCposn().isEmpty()) { rmCposn = encodeStringData(model.getRmCposn(), 4, false); } Integer itemAllLen = 0; List telInfoList = TelegramInfoList.get(dispCode); for (int i = 0; i < telInfoList.size(); i++) { itemAllLen = itemAllLen + Integer.parseInt(telInfoList.get(i).getItemLength()); } String itemAllLenHexString = Integer.toHexString(itemAllLen); if (itemAllLenHexString.length() % 2 != 0) itemAllLenHexString = "0" + itemAllLenHexString; byte[] rmDataLen = DatatypeConverter.parseHexBinary(itemAllLenHexString); Integer vectorLen = itemAllLen + length; String vectorLenHexString = Integer.toHexString(vectorLen); if (vectorLenHexString.length() % 2 != 0) vectorLenHexString = "0" + vectorLenHexString; byte[] vectorLength = DatatypeConverter.parseHexBinary(vectorLenHexString); byte[] result = concatByteArrays(vectorLength, vectorDescriptor, vectorType, vectorVersion, rmTsa, rmMapset, rmMap, rmAid, rmCposn, rmDataLen); return result; } /** * Adsデータ符号化処理 *

* {@link #getTelInfo(String) getTelInfo}を実行し、電文定義Mapリストを取得する。以下引数
*  ・引数の出力画面コード
* Adsデータ符号化バイト配列をデータ長12のバイト配列で初期化する。
* 引数のAdsデータをカンマで分割し、Ads配列とする。
* 電文定義Mapリストの件数分、以下の処理を繰り返す
*  電文定義MapのキーCHAR_DIVの値が1の場合
*   SISO除去判定をtrueに設定する。
*  電文定義MapのキーITEM_LENGTHの値を指定長に設定する。
*  リストの最終行の場合
*   データ部を指定長のバイト配列で初期化する。
*  それ以外の場合
*   {@link #decodeEbcdic(byte[]) decodeEbcdic}を実行し、取得結果をフィールドデータとする。以下引数
*    ・処理中のリストと同じインデックスのAds配列の値
*    ・指定長
*    ・SISO除去判定
*  以下の要素を連結し、Adsデータ符号化バイト配列とする。
*   ・Adsデータ符号化バイト配列
*   ・フィールド長(データ長2の空のバイト配列)
*   ・フィールド属性(データ長1の値が{-52}のバイト配列)
*   ・カラー属性(データ長1の空のバイト配列)
*   ・強調属性(データ長1の空のバイト配列)
*   ・フィールドデータ
* Adsデータ符号化バイト配列返却する。 * * @param dispCode 出力画面コード * @param adsData Adsデータ * @return adsDataを符号化したバイト配列 */ public static byte[] encodeAdsData(String dispCode, String adsData) throws IOException, UnsupportedEncodingException { byte[] result = new byte[12]; boolean isSisoNone = false; int charDiv = 0; int length = 0; String ads[] = adsData.split(",", -1); List telInfoList = TelegramInfoList.get(dispCode); for (int i = 0; i < telInfoList.size(); i++) { TelegramInfoModel telInfoModel = telInfoList.get(i); charDiv = Integer.parseInt(telInfoModel.getCharDiv().trim()); if (charDiv == 1) { isSisoNone = true; } else { isSisoNone = false; } length = Integer.parseInt(telInfoModel.getItemLength().trim()); // SJIS⇒EBCDICにコード変換する byte[] attrLength = paddingByteData(new byte[2], 2); byte[] attrFormat = paddingByteData(DEFAULT_FORMAT, 1); byte[] attrColor = paddingByteData(DEFAULT_COLOR, 1); byte[] attrTurn = paddingByteData(DEFAULT_TURN, 1); byte[] dataPart = null; if (i == telInfoList.size() - 1) { // ERRMSG1のNULL対応 dataPart = new byte[length]; } else { dataPart = encodeStringData(ads[i], length, isSisoNone); } result = concatByteArrays(result, attrLength, attrFormat, attrColor, attrTurn, dataPart); } return result; } /** * EBCDIC符号化処理 *

* 引数の文字列をEBCDICで符号化したバイト配列を返却する。
* EBCDICで符号化する際にUnsupportedEncodingExceptionが発生した場合
*  プラットフォームのデフォルト文字セットで符号化したバイト配列を返却する。 * * @param data 符号化対象文字列 * @return dataを符号化したバイト配列 */ public static byte[] encodeEbcdic(String data) { byte result[] = null; try { result = data.getBytes(EBCDIC); } catch (UnsupportedEncodingException ex) { result = data.getBytes(); } return result; } /** * EBCDIC復号処理 *

* 引数のバイト配列をEBCDICで復号して返却する。
* EBCDICで復号する際にUnsupportedEncodingExceptionが発生した場合
*  プラットフォームのデフォルト文字セットで復号して返却する。 * * @param data 符号化したバイト配列 * @return dataを復号した文字列 */ public static String decodeEbcdic(byte data[]) { String result = null; try { result = new String(data, EBCDIC); } catch (UnsupportedEncodingException ex) { result = new String(data); } return result; } /** * EBCDIC符号化処理(SISO除去) *

* {@link #encodeEbcdic(String) encodeEbcdic}を実行し、バイト配列を取得する。以下引数
*  ・引数の文字列
* バイト配列長が4以上、且つ先頭バイトがシフトイン区切り文字、且つ最終バイトがシフトアウト区切り文字の場合
*  バイト配列の先頭バイトと最終バイトを除去する。
* それ以外の場合
*  何もしない
* 上記で編集したバイト配列を返却する。 * * @param data 符号化対象文字列 * @return dataを符号化したバイト配列 */ private static byte[] encodeEbcdicSisoNone(String data) { byte sisoByte[] = encodeEbcdic(data); int sisoSize = sisoByte.length; byte result[] = null; if (sisoSize >= 4 && sisoByte[0] == SI && sisoByte[sisoSize - 1] == SO) { int size = sisoSize - 2; result = new byte[size]; for (int i = 0; i < size; i++) result[i] = sisoByte[i + 1]; } else { result = sisoByte; } return result; } /** * EBCDIC復号処理(SISO付与) *

* 引数のバイト配列長が2以上の場合
*  引数のバイト配列にSISOを付与する。
* それ以外の場合
*  何もしない
* {@link #decodeEbcdic(byte[]) decodeEbcdic}を実行し、実行結果を返却する。以下引数
*  ・上記で編集したバイト配列 * * @param data 符号化したバイト配列 * @return dataを復号した文字列 * */ private static String decodeEbcdicSisoNone(byte data[]) { byte sisoByte[] = null; int dataSize = data.length; int size = dataSize; if (dataSize >= 2) { sisoByte = new byte[size += 2]; int idx = 0; sisoByte[idx++] = SI; for (int i = 0; i < dataSize; i++) sisoByte[idx++] = data[i]; sisoByte[idx] = SO; } else { sisoByte = data; } return decodeEbcdic(sisoByte); } /** * Adsデータ復号処理 *

* 開始インデックスを12とする。
* 終了インデックスを12とする。
* {@link #getTelInfo(String) getTelInfo}を実行し、電文定義Mapリストを取得する。以下引数
*  ・引数の出力画面コード
* 電文定義Mapリストの件数分、以下の処理を繰り返す
*  電文定義MapのキーCHAR_DIVの値が1の場合
*   SISO除去判定をtrueに設定する。
*  電文定義MapのキーITEM_LENGTHの値を指定長に設定する。
*  終了インデックスに指定長と固定値5を加算する。
*  引数のAdsデータから開始インデックス~終了インデックスまでのバイト配列を取得する。
*  開始インデックスに終了インデックスを設定する。
*  バイト配列のインデックス3~4を取得し、カラー属性とする。
*  {@link #decodeStringData(byte[], boolean) decodeStringData}を実行し、取得結果をフィールドデータとする。以下引数
*   ・バイト配列のインデックス5~指定長+固定値5
*   ・SISO除去判定
*  Adsデータ復号文字列が空文字の場合
*   Adsデータ復号文字列にフィールドデータを設定する。
*  それ以外の場合
*   Adsデータ復号文字列、カンマ、フィールドデータを連結し、Adsデータ復号文字列に設定する。
*  カラー属性がデータ長1の空のバイト配列と異なる場合
*   エラーカラー属性マップにキー:電文定義MapのキーITEM_NAMEの値、値:フィールドデータのエントリを追加する。
* Adsデータ復号文字列を返却する * * @param dispCode 出力画面コード * @param adsData Adsデータ * @return adsDataを復号した文字列 */ public static String decodeAdsData(String dispCode, byte adsData[]) { String result = ""; boolean isSisoNone = false; int charDiv = 0; int length = 0; int m0 = 12; int m1 = 12; List telInfoList = TelegramInfoList.get(dispCode); for (int i = 0; i < telInfoList.size(); i++) { TelegramInfoModel telInfoModel = telInfoList.get(i); charDiv = Integer.parseInt(telInfoModel.getCharDiv().trim()); if (charDiv == 1) { isSisoNone = true; } else { isSisoNone = false; } length = Integer.parseInt(telInfoModel.getItemLength().trim()); m1 = m1 + length + ATTRIBUTE_LENGTH; byte[] ads = Arrays.copyOfRange(adsData, m0, m1); m0 = m1; //フィールド長 // String attrLength = decodeEbcdic(paddingByteData(Arrays.copyOfRange(ads, 0, 2), 2)); //フィールド属性 // String attrFormat = decodeEbcdic(paddingByteData(Arrays.copyOfRange(ads, 2, 3), 1)); //カラー属性 byte[] attrColorByteData = Arrays.copyOfRange(ads, 3, 4); // String attrColor = decodeEbcdic(paddingByteData(attrColorByteData, 1)); //強調属性 // String attrTurn = decodeEbcdic(paddingByteData(Arrays.copyOfRange(ads, 4, 5), 1)); //フィールドデータ String fieldData = decodeStringData(Arrays.copyOfRange(ads, 5, length + ATTRIBUTE_LENGTH), isSisoNone); if ("".equals(result)) result = fieldData; else result = result + "," + fieldData; // カラー属性エラー if (attrColorByteData[0] != DEFAULT_COLOR[0]) ERR_COLOR_MAP.put(telInfoModel.getItemName().trim(), fieldData); } return result; } /** * EBCDIC符号化処理(指定長) *

* 引数のSISO除去判定がtrueの場合
*  {@link #encodeEbcdicSisoNone(String) encodeEbcdicSisoNone}を実行し、バイト配列を取得する。以下引数
*   ・引数の符号化対象文字列
* 引数のSISO除去判定がfalseの場合
*  {@link #encodeEbcdic(String) encodeEbcdic}を実行し、バイト配列を取得する。以下引数
*   ・引数の符号化対象文字列
*
* 取得したバイト配列長が引数の指定長よりも大きい場合
*  バイト配列長が引数の指定長よりも大きい間、以下の処理を繰り返す
*   直前に使用した符号化対象文字列の最終文字を除去する。
*   引数のSISO除去判定がtrueの場合
*    {@link #encodeEbcdicSisoNone(String) encodeEbcdicSisoNone}を実行し、バイト配列を取得する。以下引数
*     ・編集した符号化対象文字列
*   引数のSISO除去判定がfalseの場合
*    {@link #encodeEbcdic(String) encodeEbcdic}を実行し、バイト配列を取得する。以下引数
*     ・編集した符号化対象文字列
* 取得したバイト配列長が引数の指定長と等しい場合
*  何もしない
* 取得したバイト配列長が引数の指定長よりも小さい場合
*  バイト配列長が引数の指定長よりも小さい間、以下の処理を繰り返す
*   引数のSISO除去判定がtrueの場合
*    {@link #encodeEbcdicSisoNone(String) encodeEbcdicSisoNone}を実行し、返却されたバイト配列から先頭バイトを取得する。以下引数
*     ・空白スペース *   引数のSISO除去判定がfalseの場合
*    {@link #encodeEbcdic(String) encodeEbcdic}を実行し、返却されたバイト配列から先頭バイトを取得する。以下引数
*     ・空白スペース *   取得した先頭バイトをバイト配列の最終バイトに追加する。
* 上記で編集したバイト配列を返却する。
* * @param data 符号化対象文字列 * @param length 指定長 * @param isSisoNone SISO除去判定 * @return dataを符号化したバイト配列 */ private static byte[] encodeStringData(String data, int length, boolean isSisoNone) { byte byteData[] = new byte[length]; byte realData[] = null; if (isSisoNone) realData = encodeEbcdicSisoNone(data); else realData = encodeEbcdic(data); int realSize = realData.length; int diff = length - realSize; if (diff < 0) for (; diff < 0; diff = length - realSize) { data = data.substring(0, data.length() - 1); if (isSisoNone) realData = encodeEbcdicSisoNone(data); else realData = encodeEbcdic(data); realSize = realData.length; } if (diff == 0) { byteData = realData; } else { int idx; for (idx = 0; idx < realSize; idx++) byteData[idx] = realData[idx]; for (; idx < length; idx++) if (isSisoNone) byteData[idx] = encodeEbcdicSisoNone(" ")[0]; else byteData[idx] = encodeEbcdic(" ")[0]; } return byteData; } /** * 符号化文字データ復号処理 *

* 引数のSISO除去判定がtrueの場合
*  {@link #decodeEbcdicSisoNone(byte[]) decodeEbcdicSisoNone}を実行し、復号文字列を取得する。以下引数
*   ・引数の復号対象バイト配列 * 引数のSISO除去判定がfalseの場合
*  {@link #decodeEbcdic(byte[]) decodeEbcdic}を実行し、復号文字列を取得する。
*   ・引数の復号対象バイト配列 * 上記で取得した復号文字列を返却する。
* * @param data 復号対象バイト配列 * @param isSisoNone SISO除去判定 * @return 復号文字列 * */ private static String decodeStringData(byte data[], boolean isSisoNone) { String result = ""; if (isSisoNone) result = decodeEbcdicSisoNone(data); else result = decodeEbcdic(data); return result; } /** * 16進文字列復号処理 *

* 引数の復号対象バイト配列の要素数分以下の処理を繰り返す
*  要素が0より小さい場合
*   要素に256を加算して16進文字列に変換する
*  それ以外の場合
*   要素を16進文字列に変換する
*  変換した16進文字列の文字数が2より小さい場合
*   変換した16進文字列の先頭に"0"を追加する
*  文字列バッファに16進文字列を追加する
* 文字列バッファを大文字に変換し、返却する。
* * @param data 復号対象バイト配列 * @return dataを復号した文字列 */ public static String decodeHexStringData(byte data[]) { int num = data.length; StringBuilder buff = new StringBuilder(); String tmp = null; for (int i = 0; i < num; i++) { if (data[i] < 0) tmp = Integer.toHexString(data[i] + 256); else tmp = Integer.toHexString(data[i]); if (tmp.length() < 2) tmp = (new StringBuilder("0")).append(tmp).toString(); buff.append(tmp); } return buff.toString().toUpperCase(); } /** * 指定長バイト配列パディング処理 *

* 返却値にデータ長が引数の指定長の空のバイト配列を設定する。
* 引数のパディング対象バイト配列のデータ長が引数の指定長と等しい場合
*  返却値に引数のパディング対象バイト配列を設定する
* それ以外の場合
*  引数のパディング対象バイト配列から、引数の指定長までの要素を返却値のバイト配列に設定する。
*  引数の指定長が引数のパディング対象バイト配列の要素数よりも大きい場合
*   返却値のバイト配列の未設定の要素に対して0を設定する。 * * @param data パディング対象バイト配列 * @param length 指定長 * @return パディングしたバイト配列 */ private static byte[] paddingByteData(byte data[], int length) { byte result[] = new byte[length]; int num = data.length; if (num == length) { result = data; } else { int idx = 0; for (idx = 0; idx < num; idx++) { if (idx >= length) break; result[idx] = data[idx]; } for (int i = idx; i < length; i++) result[i] = 0; } return result; } /** * バイト配列連結 *

* 引数の可変長バイト配列の要素数が1以下の場合
*  引数の可変長バイト配列を返却する
* 可変長のバイト配列の要素を連結したバイト配列を生成する。
* 連結したバイト配列を返却する。 * * @param arrays 可変長バイト配列 * @return arraysを連結したバイト配列 */ private static byte[] concatByteArrays(byte[]... arrays) { if (arrays.length <= 1) { return arrays[0]; } int length = 0; for (byte[] array : arrays) { length += array.length; } ByteBuffer buffer = ByteBuffer.allocate(length); for (byte[] array : arrays) { buffer.put(array); } return buffer.array(); } /** * 出力画面コード取得 *

* 引数のコードの先頭文字が"M"、且つ引数のコードの後尾が"00"、且つ引数のコードの文字数が3よりも大きい場合
*  出力画面コードに引数のコードの先頭の"M"~後尾"00"の間の文字列を設定する
* それ以外の場合
*  出力画面コードに引数のコードを設定する
* 出力画面コードを返却する
* * @param code コード * @return 出力画面コード */ public static String getDispCode(String code) { String dispCode = null; int dispCodeLen = code.length() - "M".length() - "00".length(); if (code.startsWith("M") && code.endsWith("00") && dispCodeLen > 0) { dispCode = code.substring("M".length(), code.lastIndexOf("00")); } else { dispCode = code; } return dispCode; } }