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 org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import java.io.UnsupportedEncodingException; 020import java.util.List; 021import java.util.ArrayList; 022import java.util.Arrays; 023 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 /** 項目タイプの定義変数:S:数字(前空白) {@value} */ 047 public static final int S = 1 ; // 5.6.6.0 (2013/07/05) 前空白詰めに変更 048 /** 項目タイプの定義変数:K:半角全角混在 {@value} */ 049 public static final int K = 2 ; 050 /** 項目タイプの定義変数:X9:数字(前ゼロ) {@value} */ 051 public static final int S0 = 3 ; // 5.6.6.0 (2013/07/05) 前ゼロ詰めを変更 052 053 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 054 public static final int T = -1 ; // 5.6.6.0 (2013/07/05) タブ区切り 055 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 056 public static final int T2 = -2 ; // 5.6.6.0 (2013/07/05) タブ区切り 057 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 058 public static final int T3 = -3 ; // 5.6.6.0 (2013/07/05) タブ区切り 059 /** 項目間空白配列の定義変数:T:タブ区切り {@value} */ 060 public static final int T4 = -4 ; // 5.6.6.0 (2013/07/05) タブ区切り 061 062 /** 初期 ENCODE 名 {@value} */ 063 public static final String ENCODE = "Windows-31J" ; 064 065 private final int[] addLen ; // 各データ間に追加するスペースを設定する。 066 private final int[] type ; // 各データの固定長形式。 0:半角文字 1:数字(前空白) 2:半角全角混在 3:数字(前ゼロ) 067 private final int size ; // データの個数 068 069 private int[] maxLen ; // 内部変数。各データの最大長を記憶する。 070 private String[] fillX ; // スペースの文字列 071 private String[] fillS ; // ゼロの文字列 072 private String[] addSpc ; // 内部変数。addLen で指定された文字数分の空白を管理します。 073 private final List<String[]> list = new ArrayList<>(); 074 075 /** 076 * データの項目数を指定して、オブジェクトを構築します。 077 * 078 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 079 * 080 * @param len データの項目数 081 */ 082 public FixLengthData( final int len ) { 083 size = len ; 084 addLen = new int[size]; 085 type = new int[size]; 086 maxLen = new int[size]; 087 } 088 089 /** 090 * 項目間空白配列と各項目のタイプ配列を指定して、オブジェクトを構築します。 091 * どちらも、int型配列なので、順番に注意してください。 092 * 093 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 094 * 095 * @param inAddLen データの項目間空白配列 096 * @param inType データの各項目のタイプ配列 097 * @see #setAddLength( int[] ) 098 * @see #setType( int[] ) 099 * @throws IllegalArgumentException 引数が null の場合 100 */ 101 public FixLengthData( final int[] inAddLen,final int[] inType ) { 102 if( inAddLen == null || inType == null ) { 103 final String errMsg = "項目間空白配列 または、項目のタイプ配列に、null は、指定できません。"; 104 throw new IllegalArgumentException( errMsg ); 105 } 106 107 size = inAddLen.length ; 108 109 addLen = new int[size]; 110 type = new int[size]; 111 maxLen = new int[size]; 112 113 setAddLength( inAddLen ); 114 setType( inType ); 115 } 116 117 /** 118 * データの項目に対応した、固定時の間に挿入する空白文字数を指定します。 119 * 初期値は、0 です。 120 * 121 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 122 * 123 * @param inAddLen データの項目間空白配列(可変長引数) 124 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 125 */ 126 public void setAddLength( final int... inAddLen ) { 127 if( inAddLen != null && inAddLen.length != size ) { // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 128 final String errMsg = "引数のデータ件数が、コンストラクタで指定した数と異なります。" 129 + "SIZE=[" + size + "] , 引数長=[" + inAddLen.length + "]" ; 130 throw new IllegalArgumentException( errMsg ); 131 } 132 133 System.arraycopy( inAddLen,0,addLen,0,size ); 134 } 135 136 /** 137 * データの各項目のタイプ(半角文字、数字)を指定します。 138 * X:半角文字の場合は、データを前方に、余った分を後方にスペースを埋めます。 139 * S:数字(前空白)の場合は、データを後方に、余った分を空白を前方に埋めます。 140 * S0:数字(前ゼロ)の場合は、データを後方に、余った分をゼロを前方に埋めます。 141 * K:半角全角混在の場合は、ENCODE(Windows-31J) で文字数を求めるとともに、X:半角文字と同様の処理を行います。 142 * 初期値は、X:半角文字 です。 143 * 144 * @param inType データの各項目のタイプ配列(可変長引数) 145 * @see #X 146 * @see #S 147 * @see #S0 148 * @see #K 149 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 150 */ 151 public void setType( final int... inType ) { 152 if( inType != null && inType.length != size ) { // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 153 final String errMsg = "引数のデータ件数が、コンストラクタで指定した数と異なります。" 154 + "SIZE=[" + size + "] , 引数長=[" + inType.length + "]" ; 155 throw new IllegalArgumentException( errMsg ); 156 } 157 158 System.arraycopy( inType,0,type,0,size ); 159 } 160 161 /** 162 * データの各項目に対応した配列データを設定します。 163 * 配列データを登録しながら、各項目の最大データ長をピックアップしていきます。 164 * 165 * @param inData データの各項目の配列(可変長引数) 166 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 167 */ 168 public void addListData( final String... inData ) { 169 if( inData != null && inData.length != size ) { // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。 170 final String errMsg = "引数のデータ件数が、コンストラクタで指定した数と異なります。" 171 + "SIZE=[" + size + "] , 引数長=[" + inData.length + "]" ; 172 throw new IllegalArgumentException( errMsg ); 173 } 174 175 // 最大データ長の取得のみ行っておきます。 176 try { 177 for( int i=0; i<size; i++ ) { 178 if( inData[i] != null ) { 179 // 6.1.1.0 (2015/01/17) 意味の異なる変数の使いまわしをしているので、修正する。 180 final int len = type[i] == K ? inData[i].getBytes( ENCODE ).length : inData[i].length(); 181 if( maxLen[i] < len ) { maxLen[i] = len; } 182 } 183 } 184 } 185 catch( final UnsupportedEncodingException ex ) { 186 final String errMsg = "データの変換に失敗しました。[" + ENCODE + "]" ; 187 throw new OgRuntimeException( errMsg,ex ); 188 } 189 list.add( inData ); 190 } 191 192 /** 193 * 指定の行に対する固定文字数に設定された文字列を返します。 194 * 引数の行番号は、addListData(String[])メソッドで登録された順番です。 195 * 196 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 197 * 198 * @param line 行番号(addListData で登録した順) 199 * 200 * @return 固定文字数に設定された文字列 201 * @og.rtnNotNull 202 */ 203 public String getFixData( final int line ) { 204 if( fillX == null ) { makeSpace(); } // 初期処理 205 206 final String[] data = list.get( line ); 207 final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE ); 208 for( int i=0; i<size; i++ ) { 209 final String dt = ( data[i] == null ) ? "" : data[i] ; 210 switch( type[i] ) { 211 case X: // 文字を出力してから、スペースで埋める。 212 rtn.append( dt ); 213 rtn.append( fillX[i].substring( dt.length() ) ); 214 break; 215 case S: // 空白で埋めてから、文字を出力する。 216 rtn.append( fillX[i].substring( dt.length() ) ); 217 rtn.append( dt ); 218 break; 219 case S0: // ゼロで埋めてから、文字を出力する。 220 rtn.append( fillS[i].substring( dt.length() ) ); 221 rtn.append( dt ); 222 break; 223 case K: // 全角を含む文字を出力してから、スペースで埋める。 224 try { 225 final int len = dt.getBytes( ENCODE ).length ; 226 rtn.append( dt ); 227 rtn.append( fillX[i].substring( len ) ); 228 } 229 catch( final UnsupportedEncodingException ex ) { 230 final String errMsg = "データの変換に失敗しました。[" + ENCODE + "]" ; 231 throw new OgRuntimeException( errMsg,ex ); 232 } 233 break; 234 default: // 基本的にありえない 235 final String errMsg = "不正な種別が指定されました[" + type[i] + "]" ; 236 throw new OgRuntimeException( errMsg ); 237 // break; 238 } 239 rtn.append( addSpc[i] ); // 5.6.6.0 (2013/07/05) 項目間のスペースを出力 240 } 241 return rtn.toString(); 242 } 243 244 /** 245 * データの各項目に対応した配列データを、すべて設定します。 246 * ここでは、配列の配列型データを受け取り、内部的に、addListData( String[] )を 247 * 実行しています。 248 * 簡易的なメソッドです。 249 * 250 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 251 * 252 * @param inData データの各項目の配列データの配列 253 * @see #addListData( String[] ) 254 * @throws IllegalArgumentException 引数のデータ件数が、コンストラクタで指定した数と異なる場合 255 */ 256 public void addAllListData( final String[][] inData ) { 257 for( int i=0; i<inData.length; i++ ) { 258 addListData( inData[i] ); 259 } 260 } 261 262 /** 263 * 内部登録済みのすべてのデータを連結して出力します。 264 * 連結時には、改行コードを設定しています。 265 * 266 * @og.rev 5.6.6.0 (2013/07/05) getAllFixData( StringBuilder ) を使用するように内部処理を変更 267 * 268 * @return 固定文字数に設定された文字列 269 * @og.rtnNotNull 270 * @see #getFixData( int ) 271 * @see #getAllFixData( StringBuilder ) 272 */ 273 public String getAllFixData() { 274 return getAllFixData( new StringBuilder( 1000 ) ).toString(); 275 } 276 277 /** 278 * 内部登録済みのすべてのデータを引数のStringBuilderに連結して返します。 279 * 連結時には、改行コードを設定しています。 280 * return オブジェクトは、この引数と同一のオブジェクトです。 281 * 282 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 283 * 284 * @param buf 連結に使用する StringBuilder 285 * @return 固定文字数に設定された StringBuilder(入力と同じ) 286 * @og.rtnNotNull 287 * @see #getFixData( int ) 288 * @see #getAllFixData() 289 */ 290 public StringBuilder getAllFixData( final StringBuilder buf ) { 291 final int len = list.size(); 292 for( int i=0; i<len; i++ ) { 293 buf.append( getFixData( i ) ).append( CR ); 294 } 295 296 return buf; 297 } 298 299 /** 300 * 固定文字列を作成するための種となるスペース文字列とゼロ文字列を作成します。 301 * 302 * @og.rev 5.6.6.0 (2013/07/05) addLen の代わりに、addSpc で管理します。 303 */ 304 private void makeSpace() { 305 fillX = new String[size]; 306 fillS = new String[size]; 307 addSpc = new String[size]; 308 char[] ch ; 309 310 int startCnt = 0; // 先頭からの文字数 311 for( int i=0; i<size; i++ ) { 312 // addLen に、T(タブ)が指定された場合、addSpc は、4の倍数になるように調整する。 313 startCnt += maxLen[i]; 314 int addCnt = addLen[i] ; 315 if( addCnt < 0 ) { // T,T2,T3,T4 のケース 316 // addSpc[i] = TAB[-addCnt]; 317 addCnt = -4 * addCnt - startCnt % 4; // TAB数に合わせたスペースに換算した数 6.9.7.0 (2018/05/14) PMD 318 } 319 // else { 320 ch = new char[addCnt]; 321 Arrays.fill( ch, ' ' ); 322 addSpc[i] = String.valueOf( ch ); 323 // } 324 startCnt += addCnt ; 325 326 ch = new char[maxLen[i]]; 327 switch( type[i] ) { 328 case S0: 329 Arrays.fill( ch, '0' ); 330 fillS[i] = String.valueOf( ch ); 331 break; 332 case X: 333 case S: 334 case K: 335 Arrays.fill( ch, ' ' ); 336 fillX[i] = String.valueOf( ch ); 337 break; 338 default: // 基本的にありえない 339 final String errMsg = "不正な種別が指定されました[" + type[i] + "]" ; 340 throw new OgRuntimeException( errMsg ); 341 // break; 342 } 343 } 344 } 345 346 /** 347 * 内部変数のデータと、最大値のキャッシュをクリアします。 348 * 349 * それ以外の変数(size、addLength、type)は、設定時のまま残っています。 350 * 351 */ 352 public void clear() { 353 list.clear() ; 354 maxLen = new int[size]; 355 fillX = null; // スペースの文字列 356 fillS = null; // ゼロの文字列 357 addSpc = null; // 項目間空白 358 } 359}