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.plugin.io; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.hayabusa.db.DBTableModelUtil; 021import org.opengion.hayabusa.io.AbstractTableReader; 022import org.opengion.fukurou.util.StringUtil; 023 024import jxl.Workbook; 025import jxl.WorkbookSettings; 026import jxl.Sheet; 027import jxl.Cell; 028import jxl.read.biff.BiffException; 029 030import java.io.File; 031import java.io.BufferedReader; 032import java.io.IOException; 033 034/** 035 * 【廃止】JExcelによるEXCELバイナリファイルを読み取る実装クラスです。 036 * 037 * ファイル名、シート名を指定して、データを読み取ることが可能です。 038 * 第一カラムが # で始まる行は、コメント行なので、読み飛ばします。 039 * カラム名の指定行で、カラム名が null の場合は、その列は読み飛ばします。 040 * 041 * ※POIを利用したTableReader_Excelを利用してください。 042 * 043 * @og.rev 3.5.4.8 (2004/02/23) 新規作成 044 * @og.group ファイル入力 045 * 046 * @version 4.0 047 * @author Kazuhiko Hasegawa 048 * @since JDK5.0, 049 */ 050public class TableReader_JExcel extends AbstractTableReader { 051 //* このプログラムのVERSION文字列を設定します。 {@value} */ 052 private static final String VERSION = "5.5.7.2 (2012/10/09)" ; 053 054 private String sheetName = null; // 3.5.4.2 (2003/12/15) 055 private String sheetNos = null; // 5.5.7.2 (2012/10/09) 056 private String filename = null; // 3.5.4.3 (2004/01/05) 057 058 /** 059 * DBTableModel から 各形式のデータを作成して,BufferedReader より読み取ります。 060 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 061 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 062 * このメソッドは、EXCEL 読み込み時に使用します。 063 * 064 * @og.rev 4.0.0.0 (2006/09/31) 新規追加 065 * @og.rev 5.1.6.0 (2010/05/01) columns 処理 追加 066 * @og.rev 5.1.6.0 (2010/05/01) skipRowCount , useNumber の追加 067 * @og.rev 5.2.1.0 (2010/10/01) setTableColumnValues メソッドを経由して、テーブルにデータをセットする。 068 * @og.rev 5.5.7.2 (2012/10/09) sheetNos 追加による複数シートのマージ読み取りサポート 069 * 070 * @see #isExcel() 071 */ 072 @Override 073 public void readDBTable() { 074 Workbook wb = null; 075 try { 076 WorkbookSettings settings = new WorkbookSettings(); 077 // System.gc()「ガベージコレクション」の実行をOFFに設定 078 settings.setGCDisabled(true); 079 wb = Workbook.getWorkbook(new File(filename),settings); 080 081// Sheet sheet ; 082 Sheet[] sheets ; // 5.5.7.2 (2012/10/09) 配列に変更 083 084 // 5.5.7.2 (2012/10/09) 複数シートのマージ読み取り。 sheetNos の指定が優先される。 085 if( sheetNos != null && sheetNos.length() > 0 ) { 086 String[] sheetList = StringUtil.csv2ArrayExt( sheetNos , wb.getNumberOfSheets()-1 ); // 最大シート番号は、シート数-1 087 sheets = new Sheet[sheetList.length]; 088 for( int i=0; i<sheetList.length; i++ ) { 089 sheets[i] = wb.getSheet( Integer.parseInt( sheetList[i] ) ); 090 } 091 } 092 else if( sheetName != null && sheetName.length() > 0 ) { 093 Sheet sheet = wb.getSheet( sheetName ); 094 if( sheet == null ) { 095 String errMsg = "対応するシートが存在しません。 Sheet=[" + sheetName + "]" ; 096 throw new HybsSystemException( errMsg ); 097 } 098 sheets = new Sheet[] { sheet }; 099 } 100 else { 101 Sheet sheet = wb.getSheet(0); 102 sheets = new Sheet[] { sheet }; 103 } 104 105// if( sheetName == null || sheetName.length() == 0 ) { 106// sheet = wb.getSheet(0); 107// } 108// else { 109// sheet = wb.getSheet( sheetName ); 110// if( sheet == null ) { 111// String errMsg = "対応するシートが存在しません。 Sheet=[" + sheetName + "]" ; 112// throw new HybsSystemException( errMsg ); 113// } 114// } 115 116 boolean nameNoSet = true; 117 table = DBTableModelUtil.newDBTable(); 118 119 int numberOfRows = 0; 120 JxlHeaderData data = new JxlHeaderData(); 121 122 // 5.1.6.0 (2010/05/01) columns 処理 123 data.setUseNumber( isUseNumber() ); 124 if( data.setColumns( columns ) ) { 125 nameNoSet = false; 126 table.init( data.getColumnSize() ); 127 setTableDBColumn( data.getNames() ) ; 128 } 129 130 // 5.5.7.2 (2012/10/09) 複数シートのマージ読み取り。 131 for( int i=0; i<sheets.length; i++ ) { // 5.5.7.2 (2012/10/09) シート配列を処理します。 132 Sheet sheet = sheets[i] ; // 5.5.7.2 (2012/10/09) 133 int rowCnt = sheet.getRows(); 134 int skip = getSkipRowCount(); // 5.1.6.0 (2010/05/01) 135 // for( int nIndexRow = 0; nIndexRow < rowCnt; nIndexRow++) { 136 for( int nIndexRow = skip; nIndexRow < rowCnt; nIndexRow++) { 137 Cell[] cells = sheet.getRow( nIndexRow ); 138 if( data.isSkip( cells ) ) { continue; } 139 if( nameNoSet ) { 140 nameNoSet = false; 141 table.init( data.getColumnSize() ); 142 setTableDBColumn( data.getNames() ) ; 143 } 144 145 if( numberOfRows < getMaxRowCount() ) { 146 setTableColumnValues( data.toArray( cells ) ); // 5.2.1.0 (2010/10/01) 147 // table.addColumnValues( data.toArray( cells ) ); 148 numberOfRows ++ ; 149 } 150 else { 151 table.setOverflow( true ); 152 } 153 } 154 155 // 最後まで、#NAME が見つから無かった場合 156 if( nameNoSet ) { 157 String errMsg = "最後まで、#NAME が見つかりませんでした。" 158 + HybsSystem.CR 159 + "ファイルが空か、もしくは損傷している可能性があります。" 160 + HybsSystem.CR ; 161 throw new HybsSystemException( errMsg ); 162 } 163 } 164 } 165 catch (IOException ex) { 166 String errMsg = "ファイル読込みエラー[" + filename + "]" ; 167 throw new HybsSystemException( errMsg,ex ); 168 } 169 catch (BiffException ex) { 170 String errMsg = "ファイル読込みエラー。データ形式が不正です[" + filename + "]" ; 171 throw new HybsSystemException( errMsg,ex ); 172 } 173 finally { 174 if( wb != null ) { wb.close(); } 175 } 176 } 177 178 /** 179 * DBTableModel から 各形式のデータを作成して,BufferedReader より読み取ります。 180 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 181 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 182 * 183 * @og.rev 3.5.4.3 (2004/01/05) 引数に、BufferedReader を受け取る要に変更します。 184 * @og.rev 4.0.0.0 (2006/09/31) UnsupportedOperationException を発行します。 185 * 186 * @param reader 各形式のデータ(使用していません) 187 */ 188 @Override 189 public void readDBTable( final BufferedReader reader ) { 190 String errMsg = "このクラスでは実装されていません。"; 191 throw new UnsupportedOperationException( errMsg ); 192 } 193 194 /** 195 * DBTableModelのデータとしてEXCELファイルを読み込むときのシート名を設定します。 196 * これにより、複数の形式の異なるデータを順次読み込むことや、シートを指定して 197 * 読み取ることが可能になります。 198 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。 199 * のでご注意ください。 200 * 201 * @og.rev 3.5.4.2 (2003/12/15) 新規追加 202 * 203 * @param sheetName シート名 204 */ 205 @Override 206 public void setSheetName( final String sheetName ) { 207 this.sheetName = sheetName; 208 } 209 210 /** 211 * EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。 212 * 213 * EXCEL読み込み時に複数シートをマージして取り込みます。 214 * シート番号は、0 から始まる数字で表します。 215 * ヘッダーは、最初のシートのカラム位置に合わせます。(ヘッダータイトルの自動認識はありません。) 216 * よって、指定するシートは、すべて同一レイアウトでないと取り込み時にカラムのずれが発生します。 217 * 218 * シート番号の指定は、カンマ区切りで、複数指定できます。また、N-M の様にハイフンで繋げることで、 219 * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。 220 * これらの組み合わせも可能です。( 0,1,3,5-8,10-* ) 221 * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの 222 * どちらかです。途中には、"*" は、現れません。 223 * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。 224 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。 225 * このメソッドは、isExcel() == true の場合のみ利用されます。 226 * 227 * 初期値は、0(第一シート) です。 228 * 229 * ※ このクラスでは実装されていません。 230 * 231 * @og.rev 5.5.7.2 (2012/10/09) 新規追加 232 * 233 * @param sheetNos EXCELファイルのシート番号(0から始まる) 234 * @see #setSheetName( String ) 235 */ 236 @Override 237 public void setSheetNos( final String sheetNos ) { 238 this.sheetNos = sheetNos; 239 } 240 241 /** 242 * このクラスが、EXCEL対応機能を持っているかどうかを返します。 243 * 244 * EXCEL対応機能とは、シート名のセット、読み込み元ファイルの 245 * Fileオブジェクト取得などの、特殊機能です。 246 * 本来は、インターフェースを分けるべきと考えますが、taglib クラス等の 247 * 関係があり、問い合わせによる条件分岐で対応します。 248 * 249 * @og.rev 3.5.4.3 (2004/01/05) 新規追加 250 * 251 * @return EXCEL対応機能を持っているかどうか(常にtrue) 252 */ 253 @Override 254 public boolean isExcel() { 255 return true; 256 } 257 258 /** 259 * 読み取り元ファイル名をセットします。(DIR + Filename) 260 * これは、EXCEL追加機能として実装されています。 261 * 262 * @og.rev 3.5.4.3 (2004/01/05) 新規作成 263 * 264 * @param filename 読み取り元ファイル名 265 */ 266 @Override 267 public void setFilename( final String filename ) { 268 this.filename = filename; 269 if( filename == null ) { 270 String errMsg = "ファイル名が指定されていません。" ; 271 throw new HybsSystemException( errMsg ); 272 } 273 } 274} 275 276/** 277 * EXCEL ネイティブのデータを処理する ローカルクラスです。 278 * このクラスでは、コメント行のスキップ判定、ヘッダー部のカラム名取得、 279 * 行情報(HSSFRow)から、カラムの配列の取得などを行います。 280 * 281 * @og.rev 3.5.4.8 (2004/02/23) 新規追加 282 * @og.group ファイル入力 283 * 284 * @version 4.0 285 * @author 儲 286 * @since JDK5.0, 287 */ 288class JxlHeaderData { 289 private String[] names ; 290 private int[] index; 291 private int columnSize = 0; 292 private boolean nameNoSet = true; 293 private boolean useNumber = true; 294 295 /** 296 * 行番号情報を使用するかどうか[true/false]を指定します(初期値:true)。 297 * 298 * 初期値は、true(使用する) です。 299 * 300 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 301 * 302 * @param useNumber 行番号情報 [true:使用している/false:していない] 303 */ 304 void setUseNumber( final boolean useNumber ) { 305 this.useNumber = useNumber ; 306 } 307 308 /** 309 * カラム名を外部から指定します。 310 * カラム名が、NULL でなければ、#NAME より、こちらが優先されます。 311 * カラム名は、順番に、指定する必要があります。 312 * 313 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 314 * 315 * @param columns EXCELのカラム列(CSV形式) 316 * 317 * @return true:処理実施/false:無処理 318 */ 319 boolean setColumns( final String columns ) { 320 if( columns != null && columns.length() > 0 ) { 321 names = StringUtil.csv2Array( columns ); 322 columnSize = names.length ; 323 index = new int[columnSize]; 324 int adrs = (useNumber) ? 1:0 ; // useNumber =true の場合は、1件目(No)は読み飛ばす。 325 for( int i=0; i<columnSize; i++ ) { index[i] = adrs++; } 326 nameNoSet = false; 327 328 return true; 329 } 330 return false; 331 } 332 333 /** 334 * EXCEL ネイティブのデータを処理する ローカルクラスです。 335 * このクラスでは、コメント行のスキップ判定、ヘッダー部のカラム名取得、 336 * 行情報(HSSFRow)から、カラムの配列の取得などを行います。 337 * 338 * @param cells Cell[] EXCELのセル配列(行) 339 * 340 * @return true:コメント行/false:通常行 341 */ 342 boolean isSkip( final Cell[] cells ) { 343 int size = cells.length ; 344 if( size == 0 ) { return true; } 345 346 String strText = cells[0].getContents(); 347 if( strText != null && strText.length() > 0 ) { 348 if( nameNoSet ) { 349 if( strText.equalsIgnoreCase( "#Name" ) ) { 350 makeNames( cells ); 351 nameNoSet = false; 352 return true; 353 } 354 else if( strText.charAt( 0 ) == '#' ) { 355 return true; 356 } 357 else { 358 String errMsg = "#NAME が見つかる前にデータが見つかりました。" 359 + HybsSystem.CR 360 + "可能性として、ファイルが、ネイティブExcelでない事が考えられます。" 361 + HybsSystem.CR ; 362 throw new HybsSystemException( errMsg ); 363 } 364 } 365 else { 366 if( strText.charAt( 0 ) == '#' ) { 367 return true; 368 } 369 } 370 } 371 372 return nameNoSet ; 373 } 374 375 /** 376 * EXCEL ネイティブの行のセル配列からカラム名情報を取得します。 377 * 378 * @og.rev 5.1.6.0 (2010/05/01) useNumber(行番号情報を、使用している(true)/していない(false)を指定) 379 * 380 * @param cells Cell[] EXCELの行のセル配列 381 */ 382 private void makeNames( final Cell[] cells ) { 383 int maxCnt = cells.length; 384 String[] names2 = new String[maxCnt]; 385 int[] index2 = new int[maxCnt]; 386 387 // 先頭カラムは、#NAME 属性行である。 388// for( int nIndexCell = 1; nIndexCell < maxCnt; nIndexCell++) { 389 // 先頭カラムは、#NAME 属性行であるかどうかを、useNumber で判定しておく。 390 int nFirstCell = (useNumber) ? 1:0 ; 391 for( int nIndexCell = nFirstCell; nIndexCell < maxCnt; nIndexCell++) { 392 String strText = cells[nIndexCell].getContents(); 393 394 if( strText != null && strText.length() > 0 ) { 395 names2[columnSize] = strText; 396 index2[columnSize] = nIndexCell; 397 columnSize++; 398 } 399 } 400 401 // #NAME を使用しない場合:no欄が存在しないケース 402 if( maxCnt == columnSize ) { 403 names = names2; 404 index = index2; 405 } 406 else { 407 names = new String[columnSize]; 408 index = new int[columnSize]; 409 System.arraycopy(names2, 0, names, 0, columnSize); 410 System.arraycopy(index2, 0, index, 0, columnSize); 411 } 412 } 413 414 /** 415 * カラム名情報を返します。 416 * ここでは、内部配列をそのまま返します。 417 * 418 * @return String[] カラム列配列情報 419 */ 420 String[] getNames() { 421 return names; 422 } 423 424 /** 425 * カラムサイズを返します。 426 * 427 * @return カラムサイズ 428 */ 429 int getColumnSize() { 430 return columnSize; 431 } 432 433 /** 434 * カラム名情報を返します。 435 * 436 * @param cells Cell[] EXCELの行のセル配列 437 * 438 * @return String[] カラム列配列情報 439 */ 440 String[] toArray( final Cell[] cells ) { 441 if( nameNoSet ) { 442 String errMsg = "#NAME が見つかる前にデータが見つかりました。"; 443 throw new HybsSystemException( errMsg ); 444 } 445 446 int cellSize = cells.length; 447 String[] data = new String[columnSize]; 448 for( int i=0;i<columnSize; i++ ) { 449 int indx = index[i]; 450 if( indx < cellSize ) { 451 data[i] = cells[indx].getContents(); 452 } 453 else { 454 data[i] = null; 455 } 456 } 457 458 return data; 459 } 460}