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.hayabusa.io; 017 018import java.io.BufferedReader; 019import java.util.Map; 020 021import org.opengion.fukurou.util.CSVTokenizer; 022import org.opengion.fukurou.util.StringUtil; 023import org.opengion.hayabusa.common.HybsSystem; 024import org.opengion.hayabusa.db.DBColumn; 025import org.opengion.hayabusa.db.DBTableModel; 026import org.opengion.hayabusa.resource.ResourceManager; 027import org.opengion.hayabusa.resource.CodeData; 028 029/** 030 * 指定の区切り記号(初期値:タブ区切り)ファイルの読み取りクラスです。 031 * 032 * 名前,データの入力部のみオーバーライドすれば,各種入力フォーマットに合わせた 033 * サブクラスを実現する事が可能です。 034 * 035 * @og.group ファイル入力 036 * 037 * @version 4.0 038 * @author Kazuhiko Hasegawa 039 * @since JDK5.0, 040 */ 041public abstract class AbstractTableReader implements TableReader { 042 //* このプログラムのVERSION文字列を設定します。 {@value} */ 043 private static final String VERSION = "5.5.8.5 (2012/11/27)" ; 044 045 private String separator = TAB_SEPARATOR; // 項目区切り文字 046 private ResourceManager resource = null; // 4.0.0 (2005/01/31) 047 private int maxRowCount = HybsSystem.sysInt( "DB_MAX_ROW_COUNT" ) ; 048 049 protected DBTableModel table = null; 050 protected DBColumn[] dbColumn = null; 051 052 // 3.5.4.5 (2004/01/23) カラム名の外部指定を出来る様にする。 053// private String columns = null; // 外部指定のカラム名 054 protected String columns = null; // 外部指定のカラム名 ( 4.3.4.7 (2009/01/22) protectedに変更 ) 055 private String encode = null; 056 private boolean useNumber = true; // 3.7.0.5 (2005/04/11) 057 058 private int skipRowCount = 0; // 5.1.6.0 (2010/05/01) データの読み飛ばし設定 059 private boolean useRenderer = false; // 5.2.1.0 (2010/10/01) 060 061 // 5.2.1.0 (2010/10/01) コードリソース毎のラベル逆引きマップ 062 private Map<?,?>[] maps = null; // 5.5.1.7 (2012/04/16) ワイルドカードを指定 063 064 private boolean useDebug = false; // 5.5.7.2 (2012/10/09) デバッグ情報の出力するかどうか 065 066 /** 067 * DBTableModel から 各形式のデータを作成して,BufferedReader より読み取ります。 068 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 069 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 070 * このメソッドは、EXCEL 読み込み時に使用します。 071 * 072 * @see #isExcel() 073 */ 074 abstract public void readDBTable(); 075 076 /** 077 * DBTableModel から 各形式のデータを作成して,BufferedReader より読み取ります。 078 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 079 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 080 * 081 * @param reader BufferedReaderオブジェクト 082 */ 083 abstract public void readDBTable( final BufferedReader reader ); 084 085 /** 086 * リソースマネージャーをセットします。 087 * これは、言語(ロケール)に応じた DBColumn をあらかじめ設定しておく為に 088 * 必要です。 089 * リソースマネージャーが設定されていない、または、所定のキーの DBColumn が 090 * リソースに存在しない場合は、内部で DBColumn オブジェクトを作成します。 091 * 092 * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更 093 * 094 * @param resource リソースマネージャー 095 */ 096 public void setResourceManager( final ResourceManager resource ) { 097 this.resource = resource; 098 } 099 100 /** 101 * DBColumn オブジェクトをDBTable に設定します。 102 * 103 * @og.rev 3.5.4.2 (2003/12/15) private を protected に変更。 104 * @og.rev 3.5.4.5 (2004/01/23) DBColumn 配列に値をセットします。 105 * @og.rev 5.2.1.0 (2010/10/01) useRenderer対応(コードリソース毎のラベル逆引き) 106 * @og.rev 5.9.0.0 (2015/09/04) XLSX対応でinitをここで行えるようにする。 107 * 108 * @param names カラム名配列 109 */ 110 protected void setTableDBColumn( final String[] names ) { 111 dbColumn = new DBColumn[names.length] ; // 3.5.4.5 追加 112 113 // 5.9.0.0 (2015/09/04) サイズ0の場合はinitする 114 if( table.getColumnCount() == 0){ 115 table.init( names.length ); 116 } 117 118 for( int i=0; i<names.length; i++ ) { 119 DBColumn clm = resource.makeDBColumn( names[i] ); 120 table.setDBColumn( i,clm ); 121 dbColumn[i] = clm; // 3.5.4.5 追加 122 } 123 124 if( useRenderer ) { 125 maps = new Map<?,?>[names.length]; // 5.5.1.7 (2012/04/16) ワイルドカードを指定 126 for( int i=0; i<names.length; i++ ) { 127 CodeData cd = dbColumn[i].getCodeData(); 128 if( cd != null ) { maps[i] = cd.makeLabelMap(); } 129 else { maps[i] = null; } 130 } 131 } 132 } 133 134 /** 135 * DBTableModelオブジェクトに、1行分のデータを追加します。 136 * これ自体は、メソッドの共通化による 拡張をしやすくするために用意しました。 137 * 138 * @og.rev 5.2.1.0 (2010/10/01) 新規作成 139 * 140 * @param values 1行分のデータ配列 141 */ 142 protected void setTableColumnValues( final String[] values ) { 143 if( useRenderer ) { 144 int clmSize = values.length; 145 for( int clmNo=0; clmNo<clmSize; clmNo++ ) { 146 if( maps[clmNo] != null ) { 147 String val = values[clmNo]; 148 if( val == null ) { val = ""; } 149 else { 150 String tmp = (String)maps[clmNo].get( val ); 151 if( tmp != null ) { values[clmNo] = tmp; } 152 else { 153 int sp = val.indexOf( ':' ); 154 if( sp >= 0 ) { 155 values[clmNo] = val.substring( 0,sp ); 156 } 157 } 158 } 159 } 160 } 161 } 162 163 table.addColumnValues( values ); 164 } 165 166 /** 167 * 1行のデータを テーブルモデルにセットするように分割します。 168 * 169 * なお、読込みは,NAME項目分を読み込みます。データ件数が少ない場合は、 170 * "" をセットしておきます。 171 * データの分割は、separator文字を使用します。 172 * 173 * @og.rev 3.3.3.1 (2003/07/18) ファイルリード/ライト時に後ろスペースの除去を行います。 174 * @og.rev 3.7.0.5 (2005/04/11) useNumber 属性を考慮します。 175 * 176 * @param data 1行のデータ 177 * @param clmSize カラム数 178 * 179 * @return 分割された文字列配列 180 */ 181 protected String[] readData( final String data,final int clmSize ) { 182 String[] rtnData = new String[ clmSize ]; 183 CSVTokenizer token = new CSVTokenizer( data,separator.charAt(0) ); 184 // 超イレギュラー処理 最初の separator 以前の文字は無視する。 185 // 3.7.0.5 (2005/04/11) 186 if( useNumber ) { token.nextToken(); } // 先頭は行番号のため無視する。 187 188 int clmNo = 0; 189 while( token.hasMoreTokens() ) { 190 String val = StringUtil.csvOutQuote( token.nextToken() ); 191 if( val != null && val.startsWith( "'0" ) ) { 192 rtnData[clmNo++] = StringUtil.rTrim( val.substring( 1 ) ); 193 } 194 else { 195 rtnData[clmNo++] = StringUtil.rTrim( val ); 196 } 197 if( clmNo >= clmSize ) { break; } // 3.7.0.5 (2005/04/11) 多い場合は、以降を無視する。 198 } 199 // EXCEL が、終端TABを削除してしまうため、少ない場合は埋める。 200 for( int i=clmNo; i<clmSize; i++ ) { 201 rtnData[i] = ""; 202 } 203 204 return rtnData; 205 } 206 207 /** 208 * 内部の DBTableModel を返します。 209 * 210 * @return テーブルモデル 211 */ 212 public DBTableModel getDBTableModel() { 213 return table; 214 } 215 216 /** 217 * データを読み込む場合の,区切り文字をセットします。 218 * 219 * なお,このメソッドは,サブクラスによっては,使用しない場合があります。 220 * もし,使用しないサブクラスを作成する場合は, UnsupportedOperationException 221 * を throw するように,サブクラスで実装して下さい。 222 * 223 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。 224 * 225 * @param sep 区切り文字 226 */ 227 public void setSeparator( final String sep ) { 228 if( sep != null ) { this.separator = sep; } 229 } 230 231 /** 232 * データを書き込む場合の,区切り文字を返します。 233 * 234 * @return 区切り文字 235 */ 236 public String getSeparator() { 237 return separator; 238 } 239 240 /** 241 * DBTableModelのデータとして登録する最大件数をこの値に設定します 242 * (初期値:DB_MAX_ROW_COUNT[={@og.value org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT}])。 243 * サーバーのメモリ資源と応答時間の確保の為です。 244 * 245 * @return 最大検索件数 246 */ 247 public int getMaxRowCount() { 248 return maxRowCount; 249 } 250 251 /** 252 * DBTableModelのデータとして登録する最大件数をこの値に設定します 253 * (初期値:DB_MAX_ROW_COUNT[={@og.value org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT}])。 254 * サーバーのメモリ資源と応答時間の確保の為です。 255 * 256 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。 257 * @og.rev 5.5.8.5 (2012/11/27) 0を無制限として処理します。 258 * 259 * @param maxRowCount 最大検索件数 260 */ 261 public void setMaxRowCount( final int maxRowCount ) { 262// this.maxRowCount = maxRowCount; 263 this.maxRowCount = ( maxRowCount > 0 ) ? maxRowCount : Integer.MAX_VALUE ; 264 } 265 266 /** 267 * DBTableModelのデータとしてEXCELファイルを読み込むときのシート名を設定します。 268 * これにより、複数の形式の異なるデータを順次読み込むことや、シートを指定して 269 * 読み取ることが可能になります。 270 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。 271 * のでご注意ください。 272 * このメソッドは、isExcel() == true の場合のみ利用されます。 273 * 274 * ※ このクラスでは実装されていません。 275 * 276 * @og.rev 3.5.4.2 (2003/12/15) 新規追加 277 * 278 * @param sheetName シート名 279 */ 280 public void setSheetName( final String sheetName ) { 281 String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。"; 282 throw new UnsupportedOperationException( errMsg ); 283 } 284 285 /** 286 * EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。 287 * 288 * EXCEL読み込み時に複数シートをマージして取り込みます。 289 * シート番号は、0 から始まる数字で表します。 290 * ヘッダーは、最初のシートのカラム位置に合わせます。(ヘッダータイトルの自動認識はありません。) 291 * よって、指定するシートは、すべて同一レイアウトでないと取り込み時にカラムのずれが発生します。 292 * 293 * シート番号の指定は、カンマ区切りで、複数指定できます。また、N-M の様にハイフンで繋げることで、 294 * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。 295 * これらの組み合わせも可能です。( 0,1,3,5-8,10-* ) 296 * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの 297 * どちらかです。途中には、"*" は、現れません。 298 * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。 299 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。 300 * このメソッドは、isExcel() == true の場合のみ利用されます。 301 * 302 * 初期値は、0(第一シート) です。 303 * 304 * ※ このクラスでは実装されていません。 305 * 306 * @og.rev 5.5.7.2 (2012/10/09) 新規追加 307 * 308 * @param sheetNos EXCELファイルのシート番号(0から始まる) 309 * @see #setSheetName( String ) 310 */ 311 public void setSheetNos( final String sheetNos ) { 312 String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。"; 313 throw new UnsupportedOperationException( errMsg ); 314 } 315 316 /** 317 * EXCELファイルを読み込むときのシート単位の固定値を設定するためのカラム名とアドレスを指定します。 318 * カラム名は、カンマ区切りで指定します。 319 * 対応するアドレスを、EXCEL上の行-列を0から始まる整数でカンマ区切りで指定します。 320 * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として 321 * 設定することができます。 322 * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。 323 * このメソッドは、isExcel() == true の場合のみ利用されます。 324 * 325 * @og.rev 5.5.8.2 (2012/11/09) 新規追加 326 * 327 * @param constKeys 固定値となるカラム名(CSV形式) 328 * @param constAdrs 固定値となるアドレス(行-列,行-列,・・・) 329 */ 330 public void setSheetConstData( final String constKeys,final String constAdrs ) { 331 String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。"; 332 throw new UnsupportedOperationException( errMsg ); 333 } 334 335 /** 336 * ここに指定されたカラム列に NULL が現れた時点で読み取りを中止します。 337 * 338 * これは、指定のカラムは必須という事を条件に、そのレコードだけを読み取る処理を行います。 339 * 複数Sheetの場合は、次のSheetを読みます。 340 * 現時点では、Excel の場合のみ有効です。 341 * 342 * @og.rev 5.5.8.2 (2012/11/09) 新規追加 343 * 344 * @param clm カラム列 345 */ 346 public void setNullBreakClm( final String clm ) { 347 String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。"; 348 throw new UnsupportedOperationException( errMsg ); 349 } 350 351 /** 352 * このクラスが、EXCEL対応機能を持っているかどうかを返します。 353 * 354 * EXCEL対応機能とは、シート名のセット、読み込み元ファイルの 355 * Fileオブジェクト取得などの、特殊機能です。 356 * 本来は、インターフェースを分けるべきと考えますが、taglib クラス等の 357 * 関係があり、問い合わせによる条件分岐で対応します。 358 * 359 * @og.rev 3.5.4.3 (2004/01/05) 新規追加 360 * 361 * @return EXCEL対応機能を持っているかどうか 362 */ 363 public boolean isExcel() { 364 return false; 365 } 366 367 /** 368 * 読み取り元ファイル名をセットします。(DIR + Filename) 369 * これは、EXCEL追加機能として実装されています。 370 * ※ このクラスでは実装されていません。 371 * 372 * @og.rev 3.5.4.3 (2004/01/05) 新規作成 373 * 374 * @param filename 読み取り元ファイル名 375 */ 376 public void setFilename( final String filename ) { 377 String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。"; 378 throw new UnsupportedOperationException( errMsg ); 379 } 380 381 /** 382 * 読み取り元ファイルのカラム列を、外部(タグ)より指定します。 383 * ファイルに記述された #NAME より優先して使用されます。 384 * 385 * @og.rev 3.5.4.5 (2004/01/23) 新規作成 386 * 387 * @param clms 読み取り元ファイルのカラム列(カンマ区切り文字) 388 */ 389 public void setColumns( final String clms ) { 390 columns = clms ; 391 } 392 393 /** 394 * 読み取り元ファイルのエンコード文字列を指定します。 395 * ファイルは、BufferedReader で受け取る為、本来は、エンコードは不要ですが、 396 * 固定長ファイルの読み取り時のバイトコード分割時に、指定のエンコードで 397 * 分割する必要があります。(例えば、半角文字は、Shift_JIS では、1バイト) 398 * 399 * @og.rev 3.5.4.5 (2004/01/23) 新規作成 400 * 401 * @param enc ファイルのエンコード文字列 402 */ 403 public void setEncode( final String enc ) { 404 encode = enc; 405 } 406 407 /** 408 * 読み取り元ファイルのエンコード文字列を取得します。 409 * ファイルは、BufferedReader で受け取る為、本来は、エンコードは不要ですが、 410 * 固定長ファイルの読み取り時のバイトコード分割時に、指定のエンコードで 411 * 分割する必要があります。(例えば、半角文字は、Shift_JIS では、1バイト) 412 * 413 * @og.rev 3.5.4.5 (2004/01/23) 新規作成 414 * 415 * @return ファイルのエンコード文字列 416 */ 417 protected String getEncode() { 418 return encode; 419 } 420 421 /** 422 * 行番号情報を指定[true:使用している/false:していない]します(初期値:true)。 423 * 424 * 通常のフォーマットでは、各行の先頭に行番号が出力されています。 425 * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。 426 * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの 427 * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、 428 * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。 429 * この場合、出力データのカラムの並び順が変更された場合、columns 属性も 430 * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように 431 * してください。 432 * なお、EXCEL 入力には、この設定は適用されません。(暫定対応) 433 * 初期値は、true(使用する) です。 434 * 435 * @og.rev 3.7.0.5 (2005/04/11) 新規追加 436 * 437 * @param useNumber 行番号情報 [true:使用する/false:使用しない] 438 */ 439 public void setUseNumber( final boolean useNumber ) { 440 this.useNumber = useNumber ; 441 } 442 443 /** 444 * データの読み始めの初期値を取得します。 445 * 446 * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。 447 * ファイルの先頭行が、0行としてカウントしますので、設定値は、読み飛ばす 448 * 件数になります。(1と指定すると、1件読み飛ばし、2行目から読み込みます。) 449 * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。 450 * #NAME属性や、columns 属性は、有効です。 451 * 452 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 453 * 454 * @return 読み始めの初期値 455 */ 456 public int getSkipRowCount() { 457 return skipRowCount ; 458 } 459 460 /** 461 * データの読み飛ばし件数を設定します。 462 * 463 * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。 464 * ファイルの先頭行が、0行としてカウントしますので、設定値は、読み飛ばす 465 * 件数になります。(1と指定すると、1件読み飛ばし、2行目から読み込みます。) 466 * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。 467 * #NAME属性や、columns 属性は、有効です。 468 * 469 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 470 * 471 * @param count 読み始めの初期値 472 */ 473 public void setSkipRowCount( final int count ) { 474 skipRowCount = count; 475 } 476 477 /** 478 * 読取処理でラベルをコードリソースに逆変換を行うかどうかを指定します。 479 * 480 * TableWriter_Renderer 系のクラスで出力した場合は、コードリソースがラベルで出力されます。 481 * そのファイルを読み取ると、当然、エラーになります。 482 * ここでは、コードリソースのカラムに対して、ラベルからコードを求める逆変換を行うことで、 483 * Renderer 系で出力したファイルを取り込むことができるようにします。 484 * 485 * ここでは、TableWriter 系と同様に、TableReader_Renderer 系のクラスを作るのではなく、 486 * 属性値のフラグで、制御します。 487 * 将来的には、TableWriter 系も廃止して、同様のフラグで制御するように変更する予定です。 488 * 489 * @og.rev 5.2.1.0 (2010/10/01) 新規作成 490 * 491 * @param useRenderer コードリソースのラベル変換を行うかどうかを指定 492 */ 493 public void setUseRenderer( final boolean useRenderer ) { 494 this.useRenderer = useRenderer; 495 } 496 497 /** 498 * 行番号情報を、使用している(true)/していない(false)を返します。 499 * 500 * 通常のフォーマットでは、各行の先頭に行番号が出力されています。 501 * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。 502 * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの 503 * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、 504 * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。 505 * この場合、出力データのカラムの並び順が変更された場合、columns 属性も 506 * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように 507 * してください。 508 * なお、EXCEL 入力には、この設定は適用されません。(暫定対応) 509 * 初期値は、true(使用する) です。 510 * 511 * @og.rev 3.7.0.5 (2005/04/11) 新規追加 512 * @og.rev 4.0.0.0 (2007/07/20) メソッド名変更(getUseNumber() ⇒ isUseNumber()) 513 * 514 * @return 行番号情報を、使用している(true)/していない(false)を指定 515 */ 516 protected boolean isUseNumber() { 517 return useNumber ; 518 } 519 520 /** 521 * デバッグ情報を出力するかどうかを指定します。 522 * 523 * EXCELなどを読み取る場合、シートマージで読み取ると、エラー時の行番号が、連番になるため、 524 * どのシートなのか、判らなくなります。 525 * そこで、どうしてもわからなくなった場合に備えて、デバッグ情報を出力できるようにします。 526 * 通常は使用しませんので、設定を無視します。 527 * 初期値は、false:デバッグ情報を出力しない です。 528 * 529 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 530 * 531 * @param useDebug デバッグ情報を出力するかどうかを指定 532 */ 533 public void setDebug( final boolean useDebug ) { 534 this.useDebug = useDebug; 535 } 536 537 /** 538 * デバッグ情報を出力するかどうかを取得します。 539 * 540 * EXCELなどを読み取る場合、シートマージで読み取ると、エラー時の行番号が、連番になるため、 541 * どのシートなのか、判らなくなります。 542 * そこで、どうしてもわからなくなった場合に備えて、デバッグ情報を出力できるようにします。 543 * 544 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 545 * 546 * @return デバッグ情報を出力するかどうか(true:する/false:しない) 547 */ 548 protected boolean isDebug() { 549 return useDebug ; 550 } 551}