001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import java.io.UnsupportedEncodingException; 019import java.util.List; 020import java.util.ArrayList; 021import java.util.Arrays; 022 023import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 024import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 025import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 026 027/** 028 * FixLengthData.java は、固定長データを作成するための簡易クラスです。 029 * 030 * データの項目(String[])を、それぞれの中で最大桁数にあわせて、スペース埋めします。 031 * 各項目間に、追加するスペース数は、setAddLength( int[] ) メソッドで、 032 * 各項目のタイプ(半角文字、全角混在、数字)の指定は、setType( int[] ) メソッド行います。 033 * 034 * このクラスは同期処理は保障されていません。 035 * 036 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加 037 * 038 * @version 4.0 039 * @author Kazuhiko Hasegawa 040 * @since JDK5.0, 041 */ 042public final class FixLengthData { 043 044 /** 項目タイプの定義変数:X:半角文字 {@value} */ 045// public static final int X = 0 ; 046 public static final int X1 = 0 ; // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 047 /** 項目タイプの定義変数:S:数字(前空白) {@value} */ 048// public static final int S = 1 ; // 5.6.6.0 (2013/07/05) 前空白詰めに変更 049 public static final int S1 = 1 ; // 5.6.6.0 (2013/07/05) 前空白詰めに変更 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 050 /** 項目タイプの定義変数:K:半角全角混在 {@value} */ 051// public static final int K = 2 ; 052 public static final int K1 = 2 ; // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 053 /** 項目タイプの定義変数:X9:数字(前ゼロ) {@value} */ 054 public static final int S0 = 3 ; // 5.6.6.0 (2013/07/05) 前ゼロ詰めを変更 055 056 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 057// public static final int T = -1 ; // 5.6.6.0 (2013/07/05) タブ区切り 058 public static final int T1 = -1 ; // 5.6.6.0 (2013/07/05) タブ区切り // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 059 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 060 public static final int T2 = -2 ; // 5.6.6.0 (2013/07/05) タブ区切り 061 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 062 public static final int T3 = -3 ; // 5.6.6.0 (2013/07/05) タブ区切り 063 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 064 public static final int T4 = -4 ; // 5.6.6.0 (2013/07/05) タブ区切り 065 066 /** 初期 ENCODE 名 {@value} */ 067 public static final String ENCODE = "Windows-31J" ; 068 069 private final int[] addLen ; // 各データ間に追加するスペースを設定する。 070 private final int[] type ; // 各データの固定長形式。 0:半角文字 1:数字(前空白) 2:半角全角混在 3:数字(前ゼロ) 071 private final int size ; // データの個数 072 073 private int[] maxLen ; // 内部変数。各データの最大長を記憶する。 074 private String[] fillX ; // スペースの文字列 075 private String[] fillS ; // ゼロの文字列 076 private String[] addSpc ; // 内部変数。addLen で指定された文字数分の空白を管理します。 077 private final List<String[]> list = new ArrayList<>(); 078 079 /** 080 * データの項目数を指定して、オブジェクトを構築します。 081 * 082 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 083 * 084 * @param len データの項目数 085 */ 086 public FixLengthData( final int len ) { 087 size = len ; 088 addLen = new int[size]; 089 type = new int[size]; 090 maxLen = new int[size]; 091 } 092 093 /** 094 * 項目間空白配列と各項目のタイプ配列を指定して、オブジェクトを構築します。 095 * どちらも、int型配列なので、順番に注意してください。 096 * 097 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 098 * 099 * @param inAddLen データの項目間空白配列 100 * @param inType データの各項目のタイプ配列 101 * @see #setAddLength( int[] ) 102 * @see #setType( int[] ) 103 * @throws IllegalArgumentException 引数が null の場合 104 */ 105 public FixLengthData( final int[] inAddLen,final int[] inType ) { 106 if( inAddLen == null || inType == null ) { 107 final String errMsg = "項目間空白配列 または、項目のタイプ配列に、null は、指定できません。"; 108 throw new IllegalArgumentException( errMsg ); 109 } 110 111 size = inAddLen.length ; 112 113 addLen = new int[size]; 114 type = new int[size]; 115 maxLen = new int[size]; 116 117 setAddLength( inAddLen ); 118 setType( inType ); 119 } 120 121 /** 122 * データの項目に対応した、固定時の間に挿入する空白文字数を指定します。 123 * 初期値は、0 です。 124 * 125 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 126 * 127 * @param inAddLen データの項目間空白配列(可変長引数) 128 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 129 */ 130 public void setAddLength( final int... inAddLen ) { 131 if( inAddLen != null && inAddLen.length != size ) { // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 132 final String errMsg = "引数のデータ件数が、コンストラクタで指定した数と異なります。" 133 + "SIZE=[" + size + "] , 引数長=[" + inAddLen.length + "]" ; 134 throw new IllegalArgumentException( errMsg ); 135 } 136 137 System.arraycopy( inAddLen,0,addLen,0,size ); 138 } 139 140 /** 141 * データの各項目のタイプ(半角文字、数字)を指定します。 142 * X:半角文字の場合は、データを前方に、余った分を後方にスペースを埋めます。 143 * S:数字(前空白)の場合は、データを後方に、余った分を空白を前方に埋めます。 144 * S0:数字(前ゼロ)の場合は、データを後方に、余った分をゼロを前方に埋めます。 145 * K:半角全角混在の場合は、ENCODE(Windows-31J) で文字数を求めるとともに、X:半角文字と同様の処理を行います。 146 * 初期値は、X:半角文字 です。 147 * 148 * @param inType データの各項目のタイプ配列(可変長引数) 149 * @see #X1 150 * @see #S1 151 * @see #S0 152 * @see #K1 153 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 154 */ 155 public void setType( final int... inType ) { 156 if( inType != null && inType.length != size ) { // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 157 final String errMsg = "引数のデータ件数が、コンストラクタで指定した数と異なります。" 158 + "SIZE=[" + size + "] , 引数長=[" + inType.length + "]" ; 159 throw new IllegalArgumentException( errMsg ); 160 } 161 162 System.arraycopy( inType,0,type,0,size ); 163 } 164 165 /** 166 * データの各項目に対応した配列データを設定します。 167 * 配列データを登録しながら、各項目の最大データ長をピックアップしていきます。 168 * 169 * @param inData データの各項目の配列(可変長引数) 170 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 171 */ 172 public void addListData( final String... inData ) { 173 if( inData != null && inData.length != size ) { // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 174 final String errMsg = "引数のデータ件数が、コンストラクタで指定した数と異なります。" 175 + "SIZE=[" + size + "] , 引数長=[" + inData.length + "]" ; 176 throw new IllegalArgumentException( errMsg ); 177 } 178 179 // 最大データ長の取得のみ行っておきます。 180 try { 181 for( int i=0; i<size; i++ ) { 182 if( inData[i] != null ) { 183 // 6.1.1.0 (2015/01/17) 意味の異なる変数の使いまわしをしているので、修正する。 184 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 185// final int len = type[i] == K ? inData[i].getBytes( ENCODE ).length : inData[i].length(); 186 final int len = type[i] == K1 ? inData[i].getBytes( ENCODE ).length : inData[i].length(); 187 if( maxLen[i] < len ) { maxLen[i] = len; } 188 } 189 } 190 } 191 catch( final UnsupportedEncodingException ex ) { 192 final String errMsg = "データの変換に失敗しました。[" + ENCODE + "]" ; 193 throw new OgRuntimeException( errMsg,ex ); 194 } 195 list.add( inData ); 196 } 197 198 /** 199 * 指定の行に対する固定文字数に設定された文字列を返します。 200 * 引数の行番号は、addListData(String[])メソッドで登録された順番です。 201 * 202 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 203 * @og.rev 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 204 * 205 * @param line 行番号(addListData で登録した順) 206 * 207 * @return 固定文字数に設定された文字列 208 * @og.rtnNotNull 209 */ 210 public String getFixData( final int line ) { 211 if( fillX == null ) { makeSpace(); } // 初期処理 212 213 final String[] data = list.get( line ); 214 final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE ); 215 for( int i=0; i<size; i++ ) { 216 final String dt = data[i] == null ? "" : data[i] ; 217 // 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 218// switch( type[i] ) { 219// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。 220// case X1: // 文字を出力してから、スペースで埋める。 221// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 222// rtn.append( dt ) 223// .append( fillX[i].substring( dt.length() ) ); 224// break; 225// case S1: // 空白で埋めてから、文字を出力する。 226// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 227// rtn.append( fillX[i].substring( dt.length() ) ) 228// .append( dt ); 229// break; 230// case S0: // ゼロで埋めてから、文字を出力する。 231// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 232// rtn.append( fillS[i].substring( dt.length() ) ) 233// .append( dt ); 234// break; 235// case K1: // 全角を含む文字を出力してから、スペースで埋める。 236// try { 237// final int len = dt.getBytes( ENCODE ).length ; 238// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 239// rtn.append( dt ) 240// .append( fillX[i].substring( len ) ); 241// } 242// catch( final UnsupportedEncodingException ex ) { 243// final String errMsg = "データの変換に失敗しました。[" + ENCODE + "]" ; 244// throw new OgRuntimeException( errMsg,ex ); 245// } 246// break; 247// default: // 基本的にありえない 248// final String errMsg = "不正な種別が指定されました[" + type[i] + "]" ; 249// throw new OgRuntimeException( errMsg ); 250// // break; 251// } 252 switch( type[i] ) { 253 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。 254 case X1 -> { // 文字を出力してから、スペースで埋める。 255 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 256 rtn.append( dt ) 257 .append( fillX[i].substring( dt.length() ) ); 258 } 259 case S1 -> { // 空白で埋めてから、文字を出力する。 260 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 261 rtn.append( fillX[i].substring( dt.length() ) ) 262 .append( dt ); 263 } 264 case S0 -> { // ゼロで埋めてから、文字を出力する。 265 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 266 rtn.append( fillS[i].substring( dt.length() ) ) 267 .append( dt ); 268 } 269 case K1 -> { // 全角を含む文字を出力してから、スペースで埋める。 270 try { 271 final int len = dt.getBytes( ENCODE ).length ; 272 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 273 rtn.append( dt ) 274 .append( fillX[i].substring( len ) ); 275 } 276 catch( final UnsupportedEncodingException ex ) { 277 final String errMsg = "データの変換に失敗しました。[" + ENCODE + "]" ; 278 throw new OgRuntimeException( errMsg,ex ); 279 } 280 } 281 default -> { // 基本的にありえない 282 final String errMsg = "不正な種別が指定されました[" + type[i] + "]" ; 283 throw new OgRuntimeException( errMsg ); 284 } 285 } 286 rtn.append( addSpc[i] ); // 5.6.6.0 (2013/07/05) 項目間のスペースを出力 287 } 288 return rtn.toString(); 289 } 290 291 /** 292 * データの各項目に対応した配列データを、すべて設定します。 293 * ここでは、配列の配列型データを受け取り、内部的に、addListData( String[] )を 294 * 実行しています。 295 * 簡易的なメソッドです。 296 * 297 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 298 * 299 * @param inData データの各項目の配列データの配列 300 * @see #addListData( String[] ) 301 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 302 */ 303 public void addAllListData( final String[][] inData ) { 304 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 305// for( int i=0; i<inData.length; i++ ) { 306// addListData( inData[i] ); 307// } 308 for( final String[] data : inData ) { 309 addListData( data ); 310 } 311 } 312 313 /** 314 * 内部登録済みのすべてのデータを連結した StringBuilder を返します。 315 * 連結時には、改行コードを設定しています。 316 * 317 * @og.rev 5.6.6.0 (2013/07/05) getAllFixData( StringBuilder ) を使用するように内部処理を変更 318 * @og.rev 8.5.6.1 (2024/03/29) StringBuilder を直接返すように内部処理を書き換えます。 319 * 320 * @return 固定文字数に設定されたStringBuilder 321 * @og.rtnNotNull 322 * @see #getFixData( int ) 323 */ 324// public String getAllFixData() { 325 public StringBuilder getAllFixData() { 326// return getAllFixData( new StringBuilder( 1000 ) ).toString(); 327 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 328 329 final int len = list.size(); 330 for( int i=0; i<len; i++ ) { 331 buf.append( getFixData( i ) ).append( CR ); 332 } 333 334 return buf; 335 } 336 337// /** 338// * 内部登録済みのすべてのデータを引数のStringBuilderに連結して返します。 339// * 連結時には、改行コードを設定しています。 340// * return オブジェクトは、この引数と同一のオブジェクトです。 341// * 342// * @og.rev 5.6.6.0 (2013/07/05) 新規追加 343// * @og.rev 8.5.6.1 (2024/03/29) 廃止 344// * 345// * @param buf 連結に使用する StringBuilder 346// * @return 固定文字数に設定された StringBuilder(入力と同じ) 347// * @og.rtnNotNull 348// * @see #getFixData( int ) 349// * @see #getAllFixData() 350// */ 351// public StringBuilder getAllFixData( final StringBuilder buf ) { 352// final int len = list.size(); 353// for( int i=0; i<len; i++ ) { 354// buf.append( getFixData( i ) ).append( CR ); 355// } 356// 357// return buf; 358// } 359 360 /** 361 * 固定文字列を作成するための種となるスペース文字列とゼロ文字列を作成します。 362 * 363 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 364 * @og.rev 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 365 */ 366 private void makeSpace() { 367 fillX = new String[size]; 368 fillS = new String[size]; 369 addSpc = new String[size]; 370 char[] ch ; 371 372 int startCnt = 0; // 先頭からの文字数 373 for( int i=0; i<size; i++ ) { 374 // addLen に、T(タブ)が指定された場合、addSpc は、4の倍数になるように調整する。 375 startCnt += maxLen[i]; 376 int addCnt = addLen[i] ; 377 if( addCnt < 0 ) { // T,T2,T3,T4 のケース 378 // addSpc[i] = TAB[-addCnt]; 379 addCnt = -4 * addCnt - startCnt % 4; // TAB数に合わせたスペースに換算した数 6.9.7.0 (2018/05/14) PMD 380 } 381 // else { 382 ch = new char[addCnt]; 383 Arrays.fill( ch, ' ' ); 384 addSpc[i] = String.valueOf( ch ); 385 // } 386 startCnt += addCnt ; 387 388 ch = new char[maxLen[i]]; 389 // 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 390// switch( type[i] ) { 391// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。 392// case S0: 393// Arrays.fill( ch, '0' ); 394// fillS[i] = String.valueOf( ch ); 395// break; 396// case X1: 397// case S1: 398// case K1: 399// Arrays.fill( ch, ' ' ); 400// fillX[i] = String.valueOf( ch ); 401// break; 402// default: // 基本的にありえない 403// final String errMsg = "不正な種別が指定されました[" + type[i] + "]" ; 404// throw new OgRuntimeException( errMsg ); 405// // break; 406// } 407 switch( type[i] ) { 408 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable 直接修正します。 409 case S0 -> { 410 Arrays.fill( ch, '0' ); 411 fillS[i] = String.valueOf( ch ); 412 } 413 case X1, S1, K1 -> { 414 Arrays.fill( ch, ' ' ); 415 fillX[i] = String.valueOf( ch ); 416 } 417 default -> { // 基本的にありえない 418 final String errMsg = "不正な種別が指定されました[" + type[i] + "]" ; 419 throw new OgRuntimeException( errMsg ); 420 } 421 } 422 } 423 } 424 425 /** 426 * 内部変数のデータと、最大値のキャッシュをクリアします。 427 * 428 * それ以外の変数(size、addLength、type)は、設定時のまま残っています。 429 * 430 */ 431 public void clear() { 432 list.clear() ; 433 maxLen = new int[size]; 434 fillX = null; // スペースの文字列 435 fillS = null; // ゼロの文字列 436 addSpc = null; // 項目間空白 437 } 438}