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