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 java.io.BufferedReader; 019// import java.io.FileInputStream; 020// import java.io.IOException; 021// import java.io.InputStream; 022// import java.text.DecimalFormat; 023// import java.text.NumberFormat; 024 025// import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 026// import org.apache.poi.ss.usermodel.Cell; 027// import org.apache.poi.ss.usermodel.DateUtil; 028// import org.apache.poi.ss.usermodel.RichTextString; 029// import org.apache.poi.ss.usermodel.Row; 030// import org.apache.poi.ss.usermodel.Sheet; 031// import org.apache.poi.ss.usermodel.Workbook; 032// import org.apache.poi.ss.usermodel.WorkbookFactory; 033// import org.apache.poi.ss.usermodel.CreationHelper; 034// import org.apache.poi.ss.usermodel.FormulaEvaluator; 035 036import org.opengion.fukurou.model.ExcelModel; // 6.0.2.0 (2014/08/29) 新規追加 037import org.opengion.fukurou.util.Closer; 038import org.opengion.fukurou.util.StringUtil; 039// import org.opengion.fukurou.util.HybsDateUtil; 040 041import org.opengion.hayabusa.common.HybsSystem; 042import org.opengion.hayabusa.common.HybsSystemException; 043import org.opengion.hayabusa.db.DBTableModelUtil; 044 045/** 046 * POI による、EXCELバイナリファイルを読み取る実装クラスです。 047 * 048 * ファイル名、シート名を指定して、データを読み取ることが可能です。 049 * 第一カラムが # で始まる行は、コメント行なので、読み飛ばします。 050 * カラム名の指定行で、カラム名が null の場合は、その列は読み飛ばします。 051 * 052 * 入力形式は、openXML形式にも対応しています。 053 * ファイルの内容に応じて、.xlsと.xlsxのどちらで読み取るかは、内部的に 054 * 自動判定されます。 055 * 056 * @og.rev 3.5.4.8 (2004/02/23) 新規作成 057 * @og.rev 4.3.6.7 (2009/05/22) ooxml形式対応 058 * @og.group ファイル入力 059 * 060 * @version 4.0 061 * @author Kazuhiko Hasegawa 062 * @since JDK5.0, 063 */ 064public class TableReader_Excel extends TableReader_Default { 065 //* このプログラムのVERSION文字列を設定します。 {@value} */ 066 private static final String VERSION = "5.5.8.2 (2012/11/09)" ; 067 068 private String filename = null; // 3.5.4.3 (2004/01/05) 069 private String sheetName = null; // 3.5.4.2 (2003/12/15) 070 private String sheetNos = null; // 5.5.7.2 (2012/10/09) 071 072 private String constKeys = null; // 5.5.8.2 (2012/11/09) 固定値となるカラム名(CSV形式) 073 private String constAdrs = null; // 5.5.8.2 (2012/11/09) 固定値となるアドレス(行-列,行-列,・・・) 074 private String nullBreakClm = null; // 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件 075 076 private ExcelModel excel = null; // 6.0.2.0 (2014/08/29) 新規追加 077 078 /** 079 * DBTableModel から 各形式のデータを作成して,BufferedReader より読み取ります。 080 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 081 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 082 * このメソッドは、EXCEL 読み込み時に使用します。 083 * 084 * @og.rev 4.0.0.0 (2006/09/31) 新規追加 085 * @og.rev 5.1.6.0 (2010/05/01) columns 処理 追加 086 * @og.rev 5.1.6.0 (2010/05/01) skipRowCountの追加 087 * @og.rev 5.1.8.0 (2010/07/01) Exception をきちっと記述(InvalidFormatException) 088 * @og.rev 5.2.1.0 (2010/10/01) setTableColumnValues メソッドを経由して、テーブルにデータをセットする。 089 * @og.rev 5.5.1.2 (2012/04/06) HeaderData を try の上にだし、エラーメッセージを取得できるようにする。 090 * @og.rev 5.5.7.2 (2012/10/09) sheetNos 追加による複数シートのマージ読み取りサポート 091 * @og.rev 5.5.8.2 (2012/11/09) HeaderData に デバッグフラグを渡します。 092 * 093 * @see #isExcel() 094 */ 095 @Override 096 public void readDBTable() { 097// InputStream in = null; 098 HeaderData data = null; // 5.5.1.2 (2012/04/06) 099// try { 100 boolean isDebug = isDebug(); // 5.5.7.2 (2012/10/09) デバッグ情報 101 102 if( isDebug ) { System.out.println( " Filename=" + filename ) ; } 103 104 excel = new ExcelModel( filename,true ); 105 106// in = new FileInputStream(filename); 107 108// Workbook wb = WorkbookFactory.create(in); 109// Sheet[] sheets ; // 5.5.7.2 (2012/10/09) 配列に変更 110 int[] sheets = null; // 6.0.2.0 (2014/08/29) Sheetオブジェクトではなく、シート番号の配列 111 112// if( isDebug ) { wb = ExcelUtil.activeWorkbook( wb ); } // デバッグモード時には、エクセルのアクティブセル領域のみにシュリンクを行う 113 if( isDebug ) { excel.activeWorkbook(); } // デバッグモード時には、エクセルのアクティブセル領域のみにシュリンクを行う 114 115 // 5.5.7.2 (2012/10/09) 複数シートのマージ読み取り。 sheetNos の指定が優先される。 116 if( sheetNos != null && sheetNos.length() > 0 ) { 117// String[] sheetList = StringUtil.csv2ArrayExt( sheetNos , wb.getNumberOfSheets()-1 ); // 最大シート番号は、シート数-1 118 String[] sheetList = StringUtil.csv2ArrayExt( sheetNos , excel.getNumberOfSheets()-1 ); // 最大シート番号は、シート数-1 119// sheets = new Sheet[sheetList.length]; 120 sheets = new int[sheetList.length]; 121 for( int i=0; i<sheetList.length; i++ ) { 122// sheets[i] = wb.getSheetAt( Integer.parseInt( sheetList[i] ) ); 123 sheets[i] = Integer.parseInt( sheetList[i] ); 124 } 125 } 126 else if( sheetName != null && sheetName.length() > 0 ) { 127// Sheet sheet = wb.getSheet( sheetName ); 128 int shNo = excel.getSheetNo( sheetName ); 129// if( sheet == null ) { 130 if( shNo < 0 ) { 131 String errMsg = "対応するシートが存在しません。 Sheet=[" + sheetName + "]" ; 132 throw new HybsSystemException( errMsg ); 133 } 134// sheets = new Sheet[] { sheet }; 135 sheets = new int[] { shNo }; 136 } 137 else { 138// Sheet sheet = wb.getSheetAt(0); 139// sheets = new Sheet[] { sheet }; 140 sheets = new int[] { 0 }; 141 } 142 143 boolean nameNoSet = true; 144 table = DBTableModelUtil.newDBTable(); 145 146 int numberOfRows = 0; 147 data = new HeaderData(); // 5.5.1.2 (2012/04/06) 148 149 data.setDebug( isDebug ); // 5.5.8.2 (2012/11/09) 150 151 // 5.1.6.0 (2010/05/01) columns 処理 152 data.setUseNumber( isUseNumber() ); 153 154 // 5.5.8.2 (2012/11/09) 固定値となるカラム名(CSV形式)とアドレス(行-列,行-列,・・・)を設定 155 data.setSheetConstData( constKeys,constAdrs ); 156 157 int nullBreakClmAdrs = -1; // 5.5.8.2 (2012/11/09) nullBreakClm の DBTableModel上のアドレス。-1 は、未使用 158 if( data.setColumns( columns ) ) { 159 nameNoSet = false; 160 table.init( data.getColumnSize() ); 161 setTableDBColumn( data.getNames() ) ; 162 nullBreakClmAdrs = table.getColumnNo( nullBreakClm, false ); // 5.5.8.2 (2012/11/09) カラム番号取得。存在しなければ -1 を返す。 163 } 164 165 int skip = getSkipRowCount(); // 5.1.6.0 (2010/05/01) 166 // 5.5.7.2 (2012/10/09) 複数シートのマージ読み取り。 167 for( int i=0; i<sheets.length; i++ ) { // 5.5.7.2 (2012/10/09) シート配列を処理します。 168// Sheet sheet = sheets[i] ; // 5.5.7.2 (2012/10/09) 169 int shtNo = sheets[i] ; 170 171 String shtName = excel.getSheetName( shtNo ); 172 173 // 先に、getSheetName を実行して、内部の Sheet を確定させる必要がある。 174// data.setSheetConstValues( sheet ); // 5.5.8.2 (2012/11/09) シート単位に固定カラムの値をキャッシュする。 175 data.setSheetConstValues( excel,shtName ); // 6.0.2.0 (2014/08/29) Sheet ではなく、ExcelModel を渡します。 176 177// int nFirstRow = sheet.getFirstRowNum(); 178 int nFirstRow = excel.getFirstRowNum(); 179 if( nFirstRow < skip ) { nFirstRow = skip; } // 5.1.6.0 (2010/05/01) 180// int nLastRow = sheet.getLastRowNum(); 181 int nLastRow = excel.getLastRowNum(); 182 if( isDebug ) { // 5.5.7.2 (2012/10/09) デバッグ情報 183// System.out.println( " Debug: 行連番=" + numberOfRows + " : Sheet= " + sheet.getSheetName() + " , 開始=" + nFirstRow + " , 終了=" + nLastRow ); 184 System.out.println( " Debug: 行連番=" + numberOfRows + " : Sheet= " + shtName + " , 開始=" + nFirstRow + " , 終了=" + nLastRow ); 185 } 186 for( int nIndexRow = nFirstRow; nIndexRow <= nLastRow; nIndexRow++) { 187 // HSSFRow oRow = sheet.getRow(nIndexRow); 188// Row oRow = sheet.getRow(nIndexRow); 189 String[] vals = excel.getValues(nIndexRow); 190// if( data.isSkip( oRow ) ) { continue; } 191 if( data.isSkip( vals ) ) { continue; } // 6.0.2.0 (2014/08/29) Row ではなく、行の文字列配列を渡します。 192 if( nameNoSet ) { 193 nameNoSet = false; 194 table.init( data.getColumnSize() ); 195 setTableDBColumn( data.getNames() ) ; 196 nullBreakClmAdrs = table.getColumnNo( nullBreakClm, false ); // 5.5.8.2 (2012/11/09) カラム番号取得。存在しなければ -1 を返す。 197 } 198 199 if( numberOfRows < getMaxRowCount() ) { 200// String[] tblData = data.row2Array( oRow ); // 5.5.8.2 (2012/11/09) nullBreakClm の判定のため、一旦配列に受ける。 201 String[] tblData = data.row2Array( vals ); // 6.0.2.0 (2014/08/29) Row ではなく、行の文字列配列を渡します。 202 if( nullBreakClmAdrs >= 0 && ( tblData[nullBreakClmAdrs] == null || tblData[nullBreakClmAdrs].isEmpty() ) ) { 203 break; // nullBreakClm が null の場合は、そのSheet処理を中止する。 204 } 205 setTableColumnValues( tblData ); // 5.5.8.2 (2012/11/09) 206 numberOfRows ++ ; 207 } 208 else { 209 table.setOverflow( true ); 210 } 211 } 212 213 // 最後まで、#NAME が見つから無かった場合 214 if( nameNoSet ) { 215 String errMsg = "最後まで、#NAME が見つかりませんでした。" 216 + HybsSystem.CR 217 + "ファイルが空か、もしくは損傷している可能性があります。" 218 + HybsSystem.CR ; 219 throw new HybsSystemException( errMsg ); 220 } 221 } 222// } 223// catch ( IOException ex ) { 224// String errMsg = "ファイル読込みエラー[" + filename + "]" ; 225// if( data != null ) { errMsg = errMsg + data.getLastCellMsg(); } // 5.5.1.2 (2012/04/06) 226// throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 227// } 228// // 5.1.8.0 (2010/07/01) Exception をきちっと記述 229// catch (InvalidFormatException ex) { 230// String errMsg = "ファイル形式エラー[" + filename + "]" ; 231// if( data != null ) { errMsg = errMsg + data.getLastCellMsg(); } // 5.5.1.2 (2012/04/06) 232// throw new HybsSystemException( errMsg,ex ); 233// } 234// finally { 235// Closer.ioClose( in ); // 4.0.0 (2006/01/31) close 処理時の IOException を無視 236// } 237 } 238 239 /** 240 * DBTableModel から 各形式のデータを作成して,BufferedReader より読み取ります。 241 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 242 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 243 * 244 * @og.rev 3.5.4.3 (2004/01/05) 引数に、BufferedReader を受け取る要に変更します。 245 * @og.rev 4.0.0.0 (2006/09/31) UnsupportedOperationException を発行します。 246 * 247 * @param reader 各形式のデータ(使用していません) 248 */ 249 @Override 250 public void readDBTable( final BufferedReader reader ) { 251 String errMsg = "このクラスでは実装されていません。"; 252 throw new UnsupportedOperationException( errMsg ); 253 } 254 255 /** 256 * DBTableModelのデータとしてEXCELファイルを読み込むときのシート名を設定します。 257 * これにより、複数の形式の異なるデータを順次読み込むことや、シートを指定して 258 * 読み取ることが可能になります。 259 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。 260 * のでご注意ください。 261 * 262 * @og.rev 3.5.4.2 (2003/12/15) 新規追加 263 * 264 * @param sheetName シート名 265 */ 266 @Override 267 public void setSheetName( final String sheetName ) { 268 this.sheetName = sheetName; 269 } 270 271 /** 272 * EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。 273 * 274 * EXCEL読み込み時に複数シートをマージして取り込みます。 275 * シート番号は、0 から始まる数字で表します。 276 * ヘッダーは、最初のシートのカラム位置に合わせます。(ヘッダータイトルの自動認識はありません。) 277 * よって、指定するシートは、すべて同一レイアウトでないと取り込み時にカラムのずれが発生します。 278 * 279 * シート番号の指定は、カンマ区切りで、複数指定できます。また、N-M の様にハイフンで繋げることで、 280 * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。 281 * これらの組み合わせも可能です。( 0,1,3,5-8,10-* ) 282 * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの 283 * どちらかです。途中には、"*" は、現れません。 284 * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。 285 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。 286 * このメソッドは、isExcel() == true の場合のみ利用されます。 287 * 288 * 初期値は、0(第一シート) です。 289 * 290 * ※ このクラスでは実装されていません。 291 * 292 * @og.rev 5.5.7.2 (2012/10/09) 新規追加 293 * 294 * @param sheetNos EXCELファイルのシート番号(0から始まる) 295 * @see #setSheetName( String ) 296 */ 297 @Override 298 public void setSheetNos( final String sheetNos ) { 299 this.sheetNos = sheetNos; 300 } 301 302 /** 303 * EXCELファイルを読み込むときのシート単位の固定値を設定するためのカラム名とアドレスを指定します。 304 * カラム名は、カンマ区切りで指定します。 305 * 対応するアドレスを、EXCEL上の行-列を0から始まる整数でカンマ区切りで指定します。 306 * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として 307 * 設定することができます。 308 * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。 309 * このメソッドは、isExcel() == true の場合のみ利用されます。 310 * 311 * @og.rev 5.5.8.2 (2012/11/09) 新規追加 312 * 313 * @param constKeys 固定値となるカラム名(CSV形式) 314 * @param constAdrs 固定値となるアドレス(行-列,行-列,・・・) 315 */ 316 @Override 317 public void setSheetConstData( final String constKeys,final String constAdrs ) { 318 this.constKeys = constKeys; 319 this.constAdrs = constAdrs; 320 } 321 322 /** 323 * ここに指定されたカラム列に NULL が現れた時点で読み取りを中止します。 324 * 325 * これは、指定のカラムは必須という事を条件に、そのレコードだけを読み取る処理を行います。 326 * 複数Sheetの場合は、次のSheetを読みます。 327 * 現時点では、Excel の場合のみ有効です。 328 * 329 * @og.rev 5.5.8.2 (2012/11/09) 新規追加 330 * 331 * @param clm カラム列 332 */ 333 @Override 334 public void setNullBreakClm( final String clm ) { 335 nullBreakClm = clm; 336 } 337 338 /** 339 * このクラスが、EXCEL対応機能を持っているかどうかを返します。 340 * 341 * EXCEL対応機能とは、シート名のセット、読み込み元ファイルの 342 * Fileオブジェクト取得などの、特殊機能です。 343 * 本来は、インターフェースを分けるべきと考えますが、taglib クラス等の 344 * 関係があり、問い合わせによる条件分岐で対応します。 345 * 346 * @og.rev 3.5.4.3 (2004/01/05) 新規追加 347 * 348 * @return EXCEL対応機能を持っているかどうか(常にtrue) 349 */ 350 @Override 351 public boolean isExcel() { 352 return true; 353 } 354 355 /** 356 * 読み取り元ファイル名をセットします。(DIR + Filename) 357 * これは、EXCEL追加機能として実装されています。 358 * 359 * @og.rev 3.5.4.3 (2004/01/05) 新規作成 360 * 361 * @param filename 読み取り元ファイル名 362 */ 363 @Override 364 public void setFilename( final String filename ) { 365 this.filename = filename; 366 if( filename == null ) { 367 String errMsg = "ファイル名が指定されていません。" ; 368 throw new HybsSystemException( errMsg ); 369 } 370 } 371} 372 373/** 374 * EXCEL ネイティブのデータを処理する ローカルクラスです。 375 * このクラスでは、コメント行のスキップ判定、ヘッダー部のカラム名取得、 376 * 行情報(Row)から、カラムの配列の取得などを行います。 377 * 378 * @og.rev 3.5.4.8 (2004/02/23) 新規追加 379 * @og.group ファイル入力 380 * 381 * @version 4.0 382 * @author 儲 383 * @since JDK5.0, 384 */ 385class HeaderData { 386 private String[] names ; 387 private int[] index; // 4.3.4.0 (2008/12/01) POI3.2対応 388 private int columnSize = 0; 389 private boolean nameNoSet = true; 390 private boolean useNumber = true; 391 private boolean isDebug = false; // 5.5.8.2 (2012/11/09) 392 393 private String[] orgNames ; // 5.5.1.2 (2012/04/06) オリジナルのカラム名 394// private Cell lastCell = null; // 5.5.1.2 (2012/04/06) 最後に実行しているセルを保持(エラー時に使用する。) 395 396 // 5.5.8.2 (2012/11/09) 固定値のカラム名、DBTableModelのアドレス、Sheetの行-列番号 397 private int cnstLen = 0; // 初期値=0 の場合は、固定値を使わないという事。 398 private String[] cnstKeys ; 399 private int[] cnstIndx ; 400 private int[] cnstRowNo; 401 private int[] cnstClmNo; 402 private String[] cnstVals ; // Sheet単位の固定値のキャッシュ(シートの最初に値を取得して保持しておく) 403 404 /** 405 * デバッグ情報を、出力するかどうか[true/false]を指定します(初期値:false)。 406 * 407 * 初期値は、false(出力しない) です。 408 * 409 * @og.rev 5.5.8.2 (2012/11/09) 新規作成 410 * 411 * @param isDebug デバッグ情報 [true:出力する/false:出力しない] 412 */ 413 void setDebug( final boolean isDebug ) { 414 this.isDebug = isDebug ; 415 } 416 417 /** 418 * 行番号情報を、使用しているかどうか[true/false]を指定します(初期値:true)。 419 * 420 * 初期値は、true(使用する) です。 421 * 422 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 423 * 424 * @param useNumber 行番号情報 [true:使用している/false:していない] 425 */ 426 void setUseNumber( final boolean useNumber ) { 427 this.useNumber = useNumber ; 428 } 429 430 /** 431 * 固定値となるカラム名(CSV形式)と、constAdrs 固定値となるアドレス(行-列,行-列,・・・)を設定します。 432 * 433 * アドレスは、EXCEL上の行-列をカンマ区切りで指定します。 434 * 行列は、EXCELオブジェクトに準拠するため、0から始まる整数です。 435 * 0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。 436 * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として 437 * 設定することができます。 438 * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。 439 * このメソッドは、isExcel() == true の場合のみ利用されます。 440 * 441 * 5.7.6.3 (2014/05/23) より、 442 * ①EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。 443 * なお、A1,A2,B1 の記述は、必ず、英字1文字+数字 にしてください。(A~Zまで) 444 * ②処理中のEXCELシート名をカラムに割り当てるために、"SHEET" という記号に対応します。 445 * 例えば、sheetConstKeys="CLM,LANG,NAME" とし、sheetConstAdrs="0-0,A2,SHEET" とすると、 446 * NAMEカラムには、シート名を読み込むことができます。 447 * これは、内部処理の簡素化のためです。 448 * 449 * ちなみに、EXCELのセルに、シート名を表示させる場合の関数は、下記の様になります。 450 * =RIGHT(CELL("filename",$A$1),LEN(CELL("filename",$A$1))-FIND("]",CELL("filename",$A$1))) 451 * 452 * @param constKeys 固定値となるカラム名(CSV形式) 453 * @param constAdrs 固定値となるアドレス(行-列,行-列,・・・) 454 * 455 * @og.rev 5.5.8.2 (2012/11/09) 新規追加 456 * @og.rev 5.7.6.3 (2014/05/23) EXCEL表記(A2,B1等)の対応と、特殊記号(SHEET)の対応 457 */ 458 void setSheetConstData( final String constKeys,final String constAdrs ) { 459 if( constKeys == null || constKeys.isEmpty() ) { 460 return ; 461 } 462 463 cnstKeys = constKeys.split( "," ); 464 cnstLen = cnstKeys.length; 465 cnstIndx = new int[cnstLen]; 466 cnstRowNo = new int[cnstLen]; 467 cnstClmNo = new int[cnstLen]; 468 469 String[] row_col = constAdrs.split( "," ) ; 470 cnstRowNo = new int[cnstLen]; 471 cnstClmNo = new int[cnstLen]; 472 for( int j=0; j<cnstLen; j++ ) { 473 cnstKeys[j] = cnstKeys[j].trim(); // 前後の不要なスペースを削除 474 String rowcol = row_col[j].trim(); // 前後の不要なスペースを削除 475 476 // 5.7.6.3 (2014/05/23) EXCEL表記(A2,B1等)の対応と、特殊記号(SHEET)の対応 477 int sep = rowcol.indexOf( '-' ); 478 if( sep > 0 ) { 479 cnstRowNo[j] = Integer.parseInt( rowcol.substring( 0,sep ) ); 480 cnstClmNo[j] = Integer.parseInt( rowcol.substring( sep+1 ) ); 481 } 482 else { 483 if( "SHEET".equalsIgnoreCase( rowcol ) ) { // "SHEET" 時は、cnstRowNo をマイナスにしておきます。 484 cnstRowNo[j] = -1 ; 485 cnstClmNo[j] = -1 ; 486 } 487 else if( rowcol.length() >= 2 ) { 488 cnstRowNo[j] = Integer.parseInt( rowcol.substring( 1 ) ) -1; // C6 の場合、RowNoは、6-1=5 489 cnstClmNo[j] = rowcol.charAt(0) - 'A' ; // C6 の場合、'C'-'A'=2 490 } 491 } 492 493 if( isDebug ) { 494 System.out.println( " Debug: constKey=" + cnstKeys[j] + " : RowNo=" + cnstRowNo[j] + " , ClmNo=" + cnstClmNo[j] ); 495 } 496 } 497 } 498 499 /** 500 * カラム名を外部から指定します。 501 * カラム名が、NULL でなければ、#NAME より、こちらが優先されます。 502 * カラム名は、順番に、指定する必要があります。 503 * 504 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 505 * @og.rev 5.5.8.2 (2012/11/09) 固定値取得用の cnstIndx の設定を行う。 506 * 507 * @param columns EXCELのカラム列(CSV形式) 508 * 509 * @return true:処理実施/false:無処理 510 */ 511 boolean setColumns( final String columns ) { 512 if( columns != null && columns.length() > 0 ) { 513 names = StringUtil.csv2Array( columns ); 514 columnSize = names.length ; 515 index = new int[columnSize]; 516 int adrs = useNumber ? 1:0 ; // useNumber =true の場合は、1件目(No)は読み飛ばす。 517 // 5.5.8.2 (2012/11/09) 固定値取得用の cnstIndx の設定を行う。 518 for( int i=0; i<columnSize; i++ ) { 519 index[i] = adrs++; 520 for( int j=0; j<cnstLen; j++ ) { 521 if( names[i].equalsIgnoreCase( cnstKeys[j] ) ) { 522 cnstIndx[j] = index[i]; 523 } 524 } 525 } 526 nameNoSet = false; 527 528 return true; 529 } 530 return false; 531 } 532 533 /** 534 * EXCEL ネイティブのデータを処理する ローカルクラスです。 535 * このクラスでは、コメント行のスキップ判定、ヘッダー部のカラム名取得、 536 * 行情報(Row)から、カラムの配列の取得などを行います。 537 * 538 * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応 539 * @og.rev 6.0.2.0 (2014/08/29) Row ではなく、行の文字列配列を渡します。 540 * 541 * @param vals EXCELの行から取得した文字列配列 542 * 543 * @return true:コメント行/false:通常行 544 */ 545// boolean isSkip( Row oRow ) { 546 boolean isSkip( final String[] vals ) { 547// if( oRow == null ) { return true; } 548 if( vals == null || vals.length == 0 ) { return true; } 549 550// int nFirstCell = oRow.getFirstCellNum(); 551// Cell oCell = oRow.getCell(nFirstCell); 552// String strText = getValue( oCell ); 553 String strText = vals[0]; 554 if( strText != null && strText.length() > 0 ) { 555 if( nameNoSet ) { 556 if( "#Name".equalsIgnoreCase( strText ) ) { 557// makeNames( oRow ); 558 makeNames( vals ); // 6.0.2.0 (2014/08/29) Row ではなく、行の文字列配列を渡します。 559 nameNoSet = false; 560 return true; 561 } 562 else if( strText.charAt( 0 ) == '#' ) { 563 return true; 564 } 565 else { 566 String errMsg = "#NAME が見つかる前にデータが見つかりました。" 567 + HybsSystem.CR 568 + "可能性として、ファイルが、ネイティブExcelでない事が考えられます。" 569 + HybsSystem.CR ; 570 throw new HybsSystemException( errMsg ); 571 } 572 } 573 else { 574 if( strText.charAt( 0 ) == '#' ) { 575 return true; 576 } 577 } 578 } 579 580 return nameNoSet ; 581 } 582 583 /** 584 * EXCELの行から取得した文字列配列からカラム名情報を取得します。 585 * 586 * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応 587 * @og.rev 5.1.6.0 (2010/05/01) useNumber(行番号情報を、使用している(true)/していない(false)を指定) 588 * @og.rev 5.1.6.0 (2010/05/01) useNumber(行番号情報を、使用している(true)/していない(false)を指定) 589 * @og.rev 5.5.1.2 (2012/04/06) オリジナルのカラム名を取得 590 * @og.rev 5.5.8.2 (2012/11/09) 固定値取得用の cnstIndx の設定を行う。 591 * @og.rev 6.0.2.0 (2014/08/29) Row ではなく、行の文字列配列を渡します。 592 * 593 * @param vals EXCELの行から取得した文字列配列 594 */ 595// private void makeNames( final Row oRow ) { 596 private void makeNames( final String[] vals ) { 597 // 先頭カラムは、#NAME 属性行であるかどうかを、useNumber で判定しておく。 598 short nFirstCell = (short)( useNumber ? 1:0 ); 599// short nLastCell = oRow.getLastCellNum(); 600 601// orgNames = new String[nLastCell+1]; // 5.5.1.2 (2012/04/06) オリジナルのカラム名を取得 602 orgNames = vals; 603 604// int maxCnt = nLastCell - nFirstCell; 605 int maxCnt = vals.length - nFirstCell ; 606 String[] names2 = new String[maxCnt]; 607 int[] index2 = new int[maxCnt]; 608 609 // 先頭カラムは、#NAME 属性行である。++ で、一つ進めている。 610 // 先頭カラムは、#NAME 属性行であるかどうかを、useNumber で判定しておく。 611// for( int nIndexCell = nFirstCell; nIndexCell <= nLastCell; nIndexCell++) { 612 for( int nIndexCell = nFirstCell; nIndexCell < vals.length; nIndexCell++) { 613// Cell oCell = oRow.getCell(nIndexCell); 614// String strText = getValue( oCell ); 615 String strText = vals[nIndexCell]; 616 617// orgNames[nIndexCell] = strText; // 5.5.1.2 (2012/04/06) オリジナルのカラム名を取得 618 619 // #NAME 行が、ゼロ文字列の場合は、読み飛ばす。 620 if( strText != null && strText.length() > 0 ) { 621 names2[columnSize] = strText; 622 index2[columnSize] = nIndexCell; 623 columnSize++; 624 } 625 } 626 627 // #NAME を使用しない場合:no欄が存在しないケース 628 if( maxCnt == columnSize ) { 629 names = names2; 630 index = index2; 631 } 632 else { 633 names = new String[columnSize]; 634 index = new int[columnSize]; 635 System.arraycopy(names2, 0, names, 0, columnSize); 636 System.arraycopy(index2, 0, index, 0, columnSize); 637 } 638 639 // 5.5.8.2 (2012/11/09) 固定値取得用の cnstIndx の設定を行う。 640 if( cnstLen > 0 ) { 641 for( int i=0; i<columnSize; i++ ) { 642 for( int j=0; j<cnstLen; j++ ) { 643 if( names[i].equalsIgnoreCase( cnstKeys[j] ) ) { 644 cnstIndx[j] = index[i]; 645 } 646 } 647 } 648 } 649 } 650 651 /** 652 * カラム名情報を返します。 653 * ここでは、内部配列をそのまま返します。 654 * 655 * @return String[] カラム列配列情報 656 */ 657 String[] getNames() { 658 return names; 659 } 660 661 /** 662 * カラムサイズを返します。 663 * 664 * @return カラムサイズ 665 */ 666 int getColumnSize() { 667 return columnSize; 668 } 669 670 /** 671 * Sheet単位の固定値のキャッシュ(シートの最初に値を取得して保持しておく)を設定します。 672 * これは、シートチェンジの最初に一度呼び出しておくことで、それ以降の列取得時に 673 * 固定値を利用することで処理速度向上を目指します。 674 * 675 * "SHEET" が指定された場合は、cnstRowNo[j]=-1 が設定されている。 676 * 677 * @og.rev 5.5.8.2 (2012/11/09) 新規作成 678 * @og.rev 5.7.6.3 (2014/05/23) 特殊記号(SHEET)の対応 679 * 680 * @param excel ExcelModelオブジェクト 681 * @param shtName Sheet名 682 */ 683// void setSheetConstValues( final Sheet sheet ) { 684 void setSheetConstValues( final ExcelModel excel,final String shtName ) { 685 cnstVals = new String[cnstLen]; 686 for( int j=0; j<cnstLen; j++ ) { 687 // 5.7.6.3 (2014/05/23) 特殊記号(SHEET)の対応 688 if( cnstRowNo[j] < 0 ) { 689// cnstVals[j] = sheet.getSheetName() ; 690 cnstVals[j] = shtName ; 691 } 692 else { 693// Row oRow = sheet.getRow( cnstRowNo[j] ); 694// Cell oCell = oRow.getCell( cnstClmNo[j] ); 695// cnstVals[j] = getValue( oCell ); 696 cnstVals[j] = excel.getValue( cnstRowNo[j],cnstClmNo[j] ); 697 } 698 699 if( isDebug ) { 700// System.out.println( " Debug: Sheet=" + sheet.getSheetName() + " : RowNo=" + cnstRowNo[j] + " , ClmNo=" + cnstClmNo[j] + " , " + cnstKeys[j] + "=" + cnstVals[j] ); 701 System.out.println( " Debug: Sheet=" + shtName + " : RowNo=" + cnstRowNo[j] + " , ClmNo=" + cnstClmNo[j] + " , " + cnstKeys[j] + "=" + cnstVals[j] ); 702 } 703 } 704 } 705 706 /** 707 * カラム名情報を返します。 708 * 709 * @og.rev 5.5.8.2 (2012/11/09) 固定値の設定を行う。 710 * @og.rev 6.0.2.0 (2014/08/29) Row ではなく、行の文字列配列を渡します。 711 * 712 * @param vals 行の文字列配列 713 * @return String[] カラム列配列情報 714 */ 715// String[] row2Array( final Row oRow ) { 716 String[] row2Array( final String[] vals ) { 717 if( nameNoSet ) { 718 String errMsg = "#NAME が見つかる前にデータが見つかりました。"; 719 throw new HybsSystemException( errMsg ); 720 } 721 722 String[] data = new String[columnSize]; 723 for( int i=0;i<columnSize; i++ ) { 724// Cell oCell = oRow.getCell( index[i] ); 725// data[i] = getValue( oCell ); 726 data[i] = vals[index[i]]; 727 } 728 729 // 5.5.8.2 (2012/11/09) 固定値の設定を行う。 730 for( int j=0; j<cnstLen; j++ ) { 731 data[cnstIndx[j]] = cnstVals[j]; 732 } 733 return data; 734 } 735 736 /** 737 * セルオブジェクト(Cell)から値を取り出します。 738 * 739 * @og.rev 3.8.5.3 (2006/08/07) 取り出し方法を少し修正 740 * @og.rev 5.5.1.2 (2012/04/06) フォーマットセルを実行して、その結果を再帰的に処理する。 741 * 742 * @param oCell Cell EXCELのセルオブジェクト 743 * 744 * @return セルの値 745 */ 746// private String getValue( final Cell oCell ) { 747// lastCell = oCell; // 5.5.1.2 (2012/04/06) 今から実行するセルを取得しておきます。 748// 749// if( oCell == null ) { return null; } 750// 751// String strText = ""; 752// RichTextString richText; 753// int nCellType = oCell.getCellType(); 754// switch(nCellType) { 755// case Cell.CELL_TYPE_NUMERIC: 756// strText = getNumericTypeString( oCell ); 757// break; 758// case Cell.CELL_TYPE_STRING: 759// // POI3.0 strText = oCell.getStringCellValue(); 760// richText = oCell.getRichStringCellValue(); 761// if( richText != null ) { 762// strText = richText.getString(); 763// } 764// break; 765// case Cell.CELL_TYPE_FORMULA: 766// // POI3.0 strText = oCell.getStringCellValue(); 767// // 5.5.1.2 (2012/04/06) フォーマットセルを実行して、その結果を再帰的に処理する。 768// Workbook wb = oCell.getSheet().getWorkbook(); 769// CreationHelper crateHelper = wb.getCreationHelper(); 770// FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator(); 771// 772// try { 773// strText = getValue(evaluator.evaluateInCell(oCell)); 774// } 775// catch ( Throwable th ) { 776// String errMsg = "セルフォーマットが解析できません。[" + oCell.getCellFormula() + "]" 777// + getLastCellMsg(); 778// throw new HybsSystemException( errMsg,th ); 779// } 780// break; 781// case Cell.CELL_TYPE_BOOLEAN: 782// strText = String.valueOf(oCell.getBooleanCellValue()); 783// break; 784// case Cell.CELL_TYPE_BLANK : 785// case Cell.CELL_TYPE_ERROR: 786// break; 787// default : 788// break; 789// } 790// return strText.trim(); 791// } 792 793 /** 794 * セル値が数字の場合に、数字か日付かを判断して、対応する文字列を返します。 795 * 796 * @og.rev 3.8.5.3 (2006/08/07) 新規追加 797 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。 798 * 799 * @param oCell Cell 800 * 801 * @return 数字の場合は、文字列に変換した結果を、日付の場合は、"yyyyMMddHHmmss" 形式で返します。 802 */ 803// private String getNumericTypeString( final Cell oCell ) { 804// final String strText ; 805// 806// double dd = oCell.getNumericCellValue() ; 807// if( DateUtil.isCellDateFormatted( oCell ) ) { 808// strText = HybsDateUtil.getDate( DateUtil.getJavaDate( dd ).getTime() , "yyyyMMddHHmmss" ); // 5.5.7.2 (2012/10/09) HybsDateUtil を利用 809// } 810// else { 811// NumberFormat numFormat = NumberFormat.getInstance(); 812// if( numFormat instanceof DecimalFormat ) { 813// ((DecimalFormat)numFormat).applyPattern( "#.####" ); 814// } 815// strText = numFormat.format( dd ); 816// } 817// return strText ; 818// } 819 820 /** 821 * 最後に実行しているセル情報を返します。 822 * 823 * エラー発生時に、どのセルでエラーが発生したかの情報を取得できるようにします。 824 * 825 * @og.rev 5.5.1.2 (2012/04/06) 新規追加 826 * @og.rev 5.5.8.2 (2012/11/09) エラー情報に、シート名も追加 827 * 828 * @return 最後に実行しているセル情報の文字列 829 */ 830// String getLastCellMsg() { 831// String lastMsg = null; 832// 833// if( lastCell != null ) { 834// int rowNo = lastCell.getRowIndex(); 835// int celNo = lastCell.getColumnIndex(); 836// int no = lastCell.getColumnIndex(); 837// String shtNm = lastCell.getSheet().getSheetName(); 838// 839// 840// lastMsg = "Sheet=" + shtNm + ", Row=" + rowNo + ", Cel=" + celNo ; 841// if( orgNames != null && orgNames.length < no ) { 842// lastMsg = lastMsg + ", NAME=" + orgNames[no] ; 843// } 844// } 845// return lastMsg; 846// } 847}