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