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.model; 017 018import java.io.InputStream; 019import java.io.FileInputStream; 020import java.io.BufferedInputStream; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.io.FileOutputStream; 024import java.io.BufferedOutputStream; 025// import java.text.DecimalFormat; 026// import java.text.NumberFormat; 027import java.util.Locale; 028 029import org.apache.poi.ss.usermodel.WorkbookFactory; 030import org.apache.poi.ss.usermodel.Workbook; 031import org.apache.poi.ss.usermodel.Sheet; 032import org.apache.poi.ss.usermodel.Row; 033import org.apache.poi.ss.usermodel.Cell; 034import org.apache.poi.ss.usermodel.CellStyle; 035import org.apache.poi.ss.usermodel.IndexedColors; 036 037import org.apache.poi.ss.usermodel.CreationHelper; 038import org.apache.poi.ss.usermodel.Font; 039 040import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 041// import org.apache.poi.ss.usermodel.DateUtil; 042import org.apache.poi.ss.usermodel.RichTextString; 043// import org.apache.poi.ss.usermodel.FormulaEvaluator; 044 045import org.opengion.fukurou.util.POIUtil; 046import org.opengion.fukurou.util.Closer; 047// import org.opengion.fukurou.util.HybsDateUtil; 048 049/** 050 * POI による、EXCELバイナリファイルに対する、データモデルクラスです。 051 * 052 * 共通的な EXCEL処理 を集約しています。 053 * staticメソッドによる簡易的なアクセスの他に、順次処理も可能なように 054 * 現在アクセス中の、Workbook、Sheet、Row、Cell オブジェクトを内部で管理しています。 055 * 056 * 入力形式は、openXML形式にも対応しています。 057 * ファイルの内容に応じて、.xlsと.xlsxのどちらで読み取るかは、内部的に 058 * 自動判定されます。 059 * 060 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 061 * @og.group その他 062 * 063 * @version 6.0 064 * @author Kazuhiko Hasegawa 065 * @since JDK7.0, 066 */ 067public class ExcelModel { 068 //* このプログラムのVERSION文字列を設定します。 {@value} */ 069 private static final String VERSION = "6.0.2.0 (2014/08/29)" ; 070 071 private static final String CR = System.getProperty("line.separator"); 072 073 private String inFilename = null; // エラー発生時のキーとなる、EXCELファイル名 074 075 private Workbook wkbook = null; // 現在処理中の Workbook 076 private Sheet sheet = null; // 現在処理中の Sheet 077 private Row rowObj = null; // 現在処理中の Row 078 079 private CreationHelper createHelper = null; // poi.xssf対応 080 081 private CellStyle style = null; // Workbook のセルスタイル 082 private int maxColCount = 5 ; // 標準セル幅の5倍を最大幅とする。 083 private boolean useAutoSizeColumn = false; // カラム幅の自動調整を行うかどうか(true:行う) 084 085 /** 086 * EXCELファイルのWookbookのデータ処理モデルを作成します。 087 * 088 * ここでは、既存のファイルを読み込んで、データ処理モデルを作成しますので、 089 * ファイルがオープンできなければエラーになります。 090 * 091 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 092 * 093 * @param fname EXCELファイル名 094 * @see #ExcelModel( String , boolean ) 095 */ 096 public ExcelModel( final String fname ) { 097 this( fname,true ); 098 } 099 100 /** 101 * EXCELファイルのWookbookのデータ処理モデルを作成します。 102 * 103 * isOpen条件によって、ファイルオープン(true)か、新規作成(false)が分かれます。 104 * ファイルオープンの場合は、EXCELの読み込み以外に、追記するとか、雛形参照する 105 * 場合にも、使用します。 106 * ファイルオープンの場合は、当然、ファイルがオープンできなければエラーになります。 107 * 108 * isOpen=新規作成(false) の場合は、ファイル名の拡張子で、XSSFWorkbook か HSSFWorkbook を 109 * 判定します。.xlsx の場合⇒XSSFWorkbook オブジェクトを使用します。 110 * 111 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 112 * 113 * @param fname EXCELファイル名 114 * @param isOpen true:ファイルオープン/false:新規作成 115 * @see #ExcelModel( String ) 116 */ 117 public ExcelModel( final String fname , final boolean isOpen ) { 118 inFilename = fname; 119 if( isOpen ) { 120 // File オブジェクトでcreate すると、ファイルがオープンされたままになってしまう。 121 InputStream fileIn = null; 122 try { 123 fileIn = new BufferedInputStream( new FileInputStream( fname ) ); 124 wkbook = WorkbookFactory.create( fileIn ); 125 } 126 catch( IOException ex ) { 127 String errMsg = "ファイル読込みエラー[" + fname + "]" + CR + ex.getMessage() ; 128 throw new RuntimeException( errMsg,ex ); 129 } 130 catch( InvalidFormatException ex ) { 131 String errMsg = "ファイル形式エラー[" + fname + "]" + CR + ex.getMessage() ; 132 throw new RuntimeException( errMsg,ex ); 133 } 134 finally { 135 Closer.ioClose( fileIn ); 136 } 137 } 138 else { 139 // 新規の場合、ファイル名に.xlsxで終了した場合⇒.xlsx形式ファイル作成、その他⇒.xls形式ファイル作成 140 if( fname.toLowerCase(Locale.JAPAN).endsWith( ".xlsx" ) ) { 141 wkbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook(); 142 } 143 else { 144 wkbook = new org.apache.poi.hssf.usermodel.HSSFWorkbook(); 145 } 146 } 147 148 createHelper = wkbook.getCreationHelper(); // poi.xssf対応 149 } 150 151 /** 152 * 内部 Workbook に、フォント名、フォントサイズを設定します。 153 * fontName(フォント名)は、"MS Pゴシック" など名称になります。 154 * fontPoint は、フォントの大きさを指定します。 155 * 内部的には、setFontHeightInPoints(short)メソッドで設定します。 156 * 157 * この処理を行うと、内部の Sheet にも、ここで作成された Sheet が設定されます。 158 * 159 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 160 * 161 * @param fontName フォント名("MS Pゴシック" など。nullの場合、セットしません) 162 * @param fontPoint フォントの大きさ(0や、マイナスの場合は、セットしません) 163 */ 164 public void setFont( final String fontName , final short fontPoint ) { 165 Font font = wkbook.getFontAt((short)0); 166 if( fontName != null ) { 167 font.setFontName( fontName ); // "MS Pゴシック" など 168 } 169 if( fontPoint > 0 ) { 170 font.setFontHeightInPoints( fontPoint ); 171 } 172 173// createHelper.applyFont( font ); // Cellの雛形Fontを優先したいので、設定しません。 174 } 175 176 /** 177 * データ設定する セルに、罫線を追加します。 178 * 179 * ここで設定するのは、罫線の種類と、罫線の色ですが、内部的に固定にしています。 180 * Border=CellStyle.BORDER_THIN 181 * BorderColor=IndexedColors.BLACK 182 * 183 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 184 * 185 */ 186 public void setCellStyle() { 187 style = wkbook.createCellStyle(); 188 189 style.setBorderBottom( CellStyle.BORDER_THIN ); 190 style.setBorderLeft( CellStyle.BORDER_THIN ); 191 style.setBorderRight( CellStyle.BORDER_THIN ); 192 style.setBorderTop( CellStyle.BORDER_THIN ); 193 194 style.setBottomBorderColor( IndexedColors.BLACK.getIndex() ); 195 style.setLeftBorderColor( IndexedColors.BLACK.getIndex() ); 196 style.setRightBorderColor( IndexedColors.BLACK.getIndex() ); 197 style.setTopBorderColor( IndexedColors.BLACK.getIndex() ); 198 } 199 200 /** 201 * Sheetに対して、autoSizeColumn設定を行うかどうか指定します。 202 * 203 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 204 * 初期カラム幅の5倍を限度にしています。 205 * 206 * なお、autoSizeColumn設定は負荷の大きな処理なので、saveFile(String)の 207 * 中で実行されます。(セーブしなければ実行されません。) 208 * 209 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 210 * 211 * @param flag true:自動カラム幅設定を行う / false:行わない。 212 * @see #useAutoSizeColumn( boolean,int ) 213 */ 214 public void useAutoSizeColumn( final boolean flag ) { 215 this.useAutoSizeColumn( flag , maxColCount ); 216 } 217 218 /** 219 * Sheetに対して、autoSizeColumn設定を行うかどうか指定します。 220 * 221 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 222 * 初期カラム幅のcount倍を限度に設定します。 223 * ただし、count がマイナスの場合は、無制限になります。 224 * 225 * なお、autoSizeColumn設定は負荷の大きな処理なので、saveFile(String)の 226 * 中で実行されます。(セーブしなければ実行されません。) 227 * 228 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 229 * 230 * @param flag true:自動カラム幅設定を行う / false:行わない。 231 * @param count 最大幅を標準セル幅の何倍にするかを指定。マイナスの場合は、無制限 232 * @see #useAutoSizeColumn( boolean ) 233 */ 234 public void useAutoSizeColumn( final boolean flag, final int count ) { 235 useAutoSizeColumn = flag; 236 maxColCount = count ; 237 } 238 239 /** 240 * 内部 Workbookの Sheet数を返します。 241 * 242 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 243 * 244 * @return シート数 245 */ 246 public int getNumberOfSheets() { 247 return wkbook.getNumberOfSheets(); 248 } 249 250 /** 251 * 内部 Workbookの 現在Sheet の最初の行番号を返します。 252 * 253 * 行は、0 から始まります。 254 * 255 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 256 * 257 * @return 最初の行番号 258 */ 259 public int getFirstRowNum() { 260 return sheet.getFirstRowNum(); 261 } 262 263 /** 264 * 内部 Workbookの 現在Sheet の最後の行番号を返します。 265 * 266 * 最終行は、含みます。よって、行数は、getLastRowNum()+1になります。 267 * 268 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 269 * 270 * @return 最後の行番号 271 */ 272 public int getLastRowNum() { 273 return sheet.getLastRowNum(); 274 } 275 276 /** 277 * 内部 Workbookより、Sheetを作ります。 278 * Sheetは、useRefName属性に応じて、参照コピーか、新規作成に分かれます。 279 * useRefName=true:参照シート使用 の場合は、refSheetName を cloneして、Sheetを作ります。 280 * refSheetName が null の場合は、第一番目のシートを clone します。 281 * useRefName=false:新規作成 の場合は、新規作成されます。 282 * 283 * この処理を行うと、内部の Sheet にも、ここで作成された Sheet が設定されます。 284 * 285 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 286 * 287 * @param refSheetName 参照シート名(nullの場合、参照シート使用する場合は、先頭のシートをコピー) 288 * @param useRefName true:参照シート使用/false:新規作成 289 */ 290 public void createSheet( final String refSheetName , final boolean useRefName ) { 291 // 参照シートを使う場合 292 if( useRefName ) { 293 // 参照シート名の指定がない場合は、最初のシート 294 int refSheetIdx = ( refSheetName == null ) ? 0 : wkbook.getSheetIndex( refSheetName ); 295 296 if( refSheetIdx < 0 ) { // 参照シート名が存在しなかった。 297 String errMsg = "指定の参照シート名は存在しませんでした。" + CR 298 + " inFilename=[" + inFilename + "] , refSheetName=[" + refSheetName + "]" + CR ; 299 throw new IllegalArgumentException( errMsg ); 300 } 301 302 sheet = wkbook.cloneSheet( refSheetIdx ); 303 } 304 else { 305 sheet = wkbook.createSheet(); 306 } 307 } 308 309 /** 310 * 内部 Workbook の指定のシート番号の Sheet の名前を設定します。 311 * 312 * 既存のシート番号に Sheet に対して名前をセットします。 313 * セットしたSheetは、内部の Sheet として設定されます。 314 * シート名の重複を避けるため、すでに、同じ名前のシートが存在する場合は、 315 * そのシート名の後に(2)、(3)のような文字列を追加します。 316 * 317 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 318 * 319 * @param stNo シート番号 320 * @param sheetName シート名(重複する場合は、(2)、(3)のような文字列を追加) 321 */ 322 public void setSheetName( final int stNo , final String sheetName ) { 323 String tempName = sheetName; 324 int cnt = 1; 325 326 while( wkbook.getSheetIndex( tempName ) >= 0 ) { // シート名が存在している場合 327 tempName = sheetName + "(" + cnt + ")"; 328 cnt++; 329 } 330 wkbook.setSheetName( stNo, tempName ); 331 sheet = wkbook.getSheetAt( stNo ); 332 } 333 334 /** 335 * 内部 Workbook のいちばん最後の Sheet の名前を設定します。 336 * 337 * 判りにくいですが、Sheet を新規作成するか、参照コピーして作成した最後の Sheet に対して 338 * 名前をセットします。 339 * シート名の重複を避けるため、すでに、同じ名前のシートが存在する場合は、 340 * そのシート名の後に(2)、(3)のような文字列を追加します。 341 * 342 * この処理を行うと、内部の Sheet にも、ここで作成された Sheet が設定されます。 343 * 344 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 345 * 346 * @param sheetName シート名(重複する場合は、(2)、(3)のような文字列を追加) 347 */ 348 public void setLastSheetName( final String sheetName ) { 349 int lastNo = wkbook.getNumberOfSheets() -1 ; // 最後のシート番号は、シート数-1 350 setSheetName( lastNo,sheetName ); 351 } 352 353 /** 354 * 内部 Workbook の 指定のSheet番号のシート名前を返します。 355 * 356 * シートが存在しない場合は、null を返します。 357 * この処理で、内部にsheetオブジェクトをキャッシュします。 358 * 359 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 360 * 361 * @param shNo シート番号 362 * 363 * @return sheetName シート名 364 */ 365 public String getSheetName( final int shNo ) { 366 int shLen = wkbook.getNumberOfSheets(); 367 368 String shName = null; 369 if( shNo < shLen ) { 370 sheet = wkbook.getSheetAt( shNo ); // 現在の sheet に設定する。 371 shName = sheet.getSheetName(); 372 } 373 374 return shName ; 375 } 376 377 /** 378 * 内部 Workbook の 指定のSheet名のシート番号を返します。 379 * 380 * シートが存在しない場合は、-1 を返します。 381 * この処理で、内部にsheetオブジェクトをキャッシュします。 382 * 383 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 384 * 385 * @param shName シート名 386 * 387 * @return シート番号(名前のシートがなめれば、-1) 388 */ 389 public int getSheetNo( final String shName ) { 390 sheet = wkbook.getSheet( shName ); // シート名がマッチしなければ、null 391 392 return wkbook.getSheetIndex( shName ) ; // シート名がマッチしなければ、-1 393 } 394 395 /** 396 * Excelの指定Sheetオブジェクトを削除します。 397 * 398 * 削除するシートは、シート番号でFrom-To形式で指定します。 399 * Fromも Toも、削除するシート番号を含みます。 400 * 例えば、0,3 と指定すると、0,1,2,3 の 4シート分を削除します。 401 * 402 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 403 * 404 * @param fromNo 削除する開始シート番号(含む) 405 * @param toNo 削除する終了シート番号(含む) 406 */ 407 public void removeSheet( final int fromNo,final int toNo ) { 408 for( int shtNo=toNo; shtNo>=fromNo; shtNo-- ) { // 逆順に処理します。 409 wkbook.removeSheetAt( shtNo ); 410 } 411 } 412 413 /** 414 * Excelの指定行のRowオブジェクトを作成します。 415 * 416 * 指定行の Row オブジェクトが存在しない場合は、新規作成します。 417 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 418 * 419 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 420 * 421 * @param rowNo 行の番号 422 */ 423 public void createRow( final int rowNo ) { 424 rowObj = sheet.getRow( rowNo ); 425 if( rowObj == null ) { rowObj = sheet.createRow( rowNo ); } 426 } 427 428 /** 429 * Excelの指定行以降の余計なRowオブジェクトを削除します。 430 * 431 * 指定行の Row オブジェクトから、getLastRowNum() までの行を、削除します。 432 * 433 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 434 * 435 * @param startRowNum 指定以降の余計な行を削除 436 */ 437 public void removeRow( final int startRowNum ) { 438 int stR = startRowNum; 439 int edR = sheet.getLastRowNum(); 440 441 for( int rowNo=edR; rowNo>=stR; rowNo-- ) { // 逆順に処理します。 442 Row rowObj = sheet.getRow( rowNo ); 443 if( rowObj != null ) { sheet.removeRow( rowObj ); } 444 } 445 } 446 447 /** 448 * Excelの処理中のRowオブジェクトの指定カラム以降の余計なCellオブジェクトを削除します。 449 * 450 * 指定行の Row オブジェクトから、getLastCellNum() までのカラムを、削除します。 451 * 452 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 453 * 454 * @param startCellNum 指定以降の余計なカラムを削除 455 */ 456 public void removeCell( final int startCellNum ) { 457 int stC = startCellNum; 458 int edC = rowObj.getLastCellNum(); 459 460 for( int colNo=edC; colNo>=stC; colNo-- ) { // 逆順に処理します。 461 Cell colObj = rowObj.getCell( colNo ); 462 if( colObj != null ) { rowObj.removeCell( colObj ); } 463 } 464 } 465 466 /** 467 * row にあるセルのオブジェクト値を設定します。 468 * 469 * 行が存在しない場合、行を追加します。 470 * 471 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 472 * 473 * @param vals 新しい配列値。 474 * @param rowNo 値が変更される行(無視されます) 475 */ 476 public void setValues( final String[] vals,final int rowNo ) { 477 if( rowObj == null ) { createRow( rowNo ); } 478 479 if( vals != null ) { 480 for( int colNo=0; colNo<vals.length; colNo++ ) { 481 setCellValue( vals[colNo],colNo ); 482 } 483 } 484 } 485 486 /** 487 * row にあるセルのオブジェクト値を設定します。 488 * 489 * 行が存在しない場合、行を追加します。 490 * 引数に、カラムがNUMBER型かどうかを指定することが出来ます。 491 * 492 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 493 * 494 * @param vals 新しい配列値。 495 * @param rowNo 値が変更される行(無視されます) 496 * @param isNums セルが、NUMBER型の場合は、true/それ以外は、false 497 */ 498 public void setValues( final String[] vals,final int rowNo,final boolean[] isNums ) { 499 if( rowObj == null ) { createRow( rowNo ); } 500 501 if( vals != null ) { 502 for( int colNo=0; colNo<vals.length; colNo++ ) { 503 setCellValue( vals[colNo],colNo,isNums[colNo] ); 504 } 505 } 506 } 507 508 /** 509 * Excelの指定セルにデータを設定します。 510 * 511 * ここで設定する行は、現在の内部 Row です。 512 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 513 * このメソッドでは、データを文字列型として設定します。 514 * 515 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 516 * 517 * @param dataVal String文字列 518 * @param colNo セルの番号(0,1,2・・・・) 519 * @see #setCellValue( String,int,boolean ) 520 */ 521 public void setCellValue( final String dataVal , final int colNo ) { 522 this.setCellValue( dataVal,colNo,false ); 523 } 524 525 /** 526 * Excelの指定セルにデータを設定します。 527 * 528 * ここで設定する行は、現在の内部 Row です。 529 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 530 * このメソッドでは、引数のデータ型をNUMBER型の場合は、doubleに変換して、 531 * それ以外は文字列としてとして設定します。 532 * 533 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 534 * 535 * @param dataVal String文字列 536 * @param colNo セルの番号(0,1,2・・・・) 537 * @param isNumber セルが、NUMBER型の場合は、true/それ以外は、false 538 * @see #createRow( int ) 539 * @see #setCellValue( String,int ) 540 */ 541 public void setCellValue( final String dataVal , final int colNo , final boolean isNumber ) { 542 Cell colObj = rowObj.getCell( colNo ); 543 if( colObj == null ) { colObj = rowObj.createCell( colNo ); } 544 545 // CELL_TYPE_NUMERIC 以外は、String扱いします。 546// if( colObj.getCellType() == Cell.CELL_TYPE_NUMERIC ) { 547 if( isNumber ) { 548 Double dbl = parseDouble( dataVal ); 549 if( dbl != null ) { 550 colObj.setCellValue( dbl.doubleValue() ); 551 return ; // Double 変換できた場合は、即抜けます。 552 } 553 } 554 555 RichTextString richText = createHelper.createRichTextString( dataVal ); 556 colObj.setCellValue( richText ); 557 558 if( style != null ) { colObj.setCellStyle(style); } 559 } 560 561 /** 562 * 現在のRow にあるセルの属性値を配列で返します。 563 * 564 * Rowオブジェクトが存在しない場合は、null を返します。 565 * また、Rowオブジェクトの中の セルオブジェクトが存在しない場合は、 566 * null がセットされます。 567 * 568 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 569 * 570 * @param rowNo 行の番号 571 * @return 指定されたセルの属性値。Rowがnullの場合は、nullを返します。 572 */ 573 public String[] getValues( final int rowNo ) { 574 rowObj = sheet.getRow( rowNo ); 575 if( rowObj == null ) { return null; } 576 577 int len = rowObj.getLastCellNum() + 1; 578 String[] vals = new String[len]; 579 580 for( int colNo=0; colNo<len; colNo++ ) { 581 Cell colObj = rowObj.getCell( colNo ); 582 vals[colNo] = POIUtil.getValue( colObj ); 583 } 584 585 return vals ; 586 } 587 588 /** 589 * 現在のrow にあるセルの属性値を返します。 590 * 591 * セルオブジェクトが存在しない場合は、null を返します。 592 * 593 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 594 * 595 * @param rowNo 値が参照される行 596 * @param colNo 値が参照される列 597 * 598 * @return 指定されたセルの値 T 599 */ 600 public String getValue( final int rowNo, final int colNo ) { 601 rowObj = sheet.getRow( rowNo ); 602 if( rowObj == null ) { return null; } 603 604 Cell colObj = rowObj.getCell( colNo ); 605 return POIUtil.getValue( colObj ); 606 } 607 608 /** 609 * 内部 Workbook オブジェクトをファイルに書き出します。 610 * 611 * 書き出すファイルの拡張子に応じて、自動的に、Excel 2007以降の形式(.xlsx)か、 612 * Excel 2003以前の形式(.xls) で出力されます。 613 * 614 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 615 * 616 * @param filename セーブするファイル名 617 */ 618 public void saveFile( final String filename ) { 619 if( useAutoSizeColumn ) { 620 int shCnt = wkbook.getNumberOfSheets(); 621 for( int shNo=0; shNo<shCnt; shNo++ ) { 622 Sheet sht = wkbook.getSheetAt( shNo ); 623 int defW = sht.getDefaultColumnWidth(); // 標準カラムの文字数 624 int maxWidth = defW*256*maxColCount ; // Widthは、文字数(文字幅)*256*最大セル数 625 626 int stR = sht.getFirstRowNum(); 627 int edR = sht.getLastRowNum(); 628 629 Row rowObj = sht.getRow( stR ); 630 int stC = rowObj.getFirstCellNum(); 631 int edC = rowObj.getLastCellNum(); 632 for( int colNo=stC; colNo<=edC; colNo++ ) { 633 sht.autoSizeColumn( colNo ); 634 if( maxWidth >= 0 ) { // マイナスや0の場合は、幅に制限をかけない。 635 int wd = sht.getColumnWidth( colNo ); 636 if( wd > maxWidth ) { sht.setColumnWidth( colNo,maxWidth ); } 637 } 638 } 639 } 640 } 641 642 OutputStream fileOut = null ; 643 try { 644 fileOut = new BufferedOutputStream( new FileOutputStream( filename ) ); 645 wkbook.write( fileOut ); 646 } 647 catch( IOException ex ) { 648 String errMsg = "ファイルへ書込み中にエラーが発生しました。" + CR 649 + " File=" + filename + CR 650 + ex.getMessage() ; 651 throw new RuntimeException( errMsg,ex ); 652 } 653 finally { 654 Closer.ioClose( fileOut ); 655 } 656 } 657 658 /** 659 * Workbook の全Sheetを対象に、空行を取り除き、全体をシュリンクします。 660 * 661 * ここでは、Row を逆順にスキャンし、Cellが 存在しない間は、行を削除します。 662 * 途中の行を削除しても、データとしては残っており、LastRowNum との矛盾の為、 663 * 最終行が表示されなくなってしまいます。 664 * よって、途中の削除は行いません。(方法が見つかれば別) 665 * 666 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 667 */ 668 public void activeWorkbook() { 669 int shCnt = wkbook.getNumberOfSheets(); 670 for( int shNo=0; shNo<shCnt; shNo++ ) { 671 Sheet sheet = wkbook.getSheetAt( shNo ); 672 673 int stR = sheet.getFirstRowNum(); 674 int edR = sheet.getLastRowNum(); 675 676 boolean isRowDel = true; // 行の削除は、Cellが見つかるまで。 677 for( int rowNo=edR; rowNo>=stR; rowNo-- ) { // 逆順に処理します。 678 Row rowObj = sheet.getRow( rowNo ); 679 if( rowObj != null ) { 680 int stC = rowObj.getFirstCellNum(); 681 int edC = rowObj.getLastCellNum(); 682 for( int colNo=edC; colNo>=stC; colNo-- ) { 683 Cell colObj = rowObj.getCell( colNo ); 684 if( colObj != null ) { 685 String val = POIUtil.getValue( colObj ); 686 if( colObj.getCellType() != Cell.CELL_TYPE_BLANK && val != null && val.length() > 0 ) { 687 isRowDel = false; // 一つでも現れれば、行の削除は中止 688 break; 689 } 690 else { 691 rowObj.removeCell( colObj ); // CELL_TYPE_BLANK の場合は、削除 692 } 693 } 694 } 695 if( isRowDel ) { sheet.removeRow( rowObj ); } 696 // else { break; } // Cell の処理を継続しない場合は、break すればよい。 697 } 698 } 699 } 700 } 701 702 /** 703 * 文字列を Double オブジェクトに変換します。 704 * 705 * これは、引数の カンマ(,) を削除した文字列から、Double オブジェクトを生成します。 706 * 処理中に、文字列が解析可能な double を含まない場合(NumberFormatException) 707 * また、引数が、null,ゼロ文字列,'_', エラー の時には、null を返します。 708 * 709 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 710 * 711 * @param value Doubleに変換する元の文字列 712 * 713 * @return 変換後のDoubleオブジェクト(エラー発生時や変換不可の場合は、null) 714 */ 715 private Double parseDouble( final String value ) { 716 Double rtn = null ; 717 718 try { 719 if( value == null || value.length() == 0 || value.equals( "_" ) ) { 720 rtn = null; 721 } 722 else if( value.indexOf( ',' ) < 0 ) { 723 rtn = new Double( value ); 724 } 725 else { 726 char[] chs = value.toCharArray() ; 727 int j=0; 728 for( int i=0;i<chs.length; i++ ) { 729 if( chs[i] == ',' ) { continue; } 730 chs[j] = chs[i]; 731 j++; 732 } 733 rtn = new Double( String.valueOf( chs,0,j ) ); 734 } 735 } 736 catch( NumberFormatException ex ) { // 文字列が解析可能な数値を含まない場合 737 String errMsg = "Double変換できませんでした。" + CR 738 + ex.getMessage() + CR 739 + " value=" + value; 740 System.err.println( errMsg ); 741 rtn = null; 742 } 743 744 return rtn ; 745 } 746 747 /** 748 * アプリケーションのサンプルです。 749 * 750 * Usage: java org.opengion.fukurou.model.ExcelModel 入力ファイル名 751 * 752 * @og.rev 6.0.2.0 (2014/08/29) 新規作成 753 * 754 * @param args コマンド引数配列 755 */ 756 public static void main( final String[] args ) { 757 if( args.length == 0 ) { 758 System.err.println( "Usage: java org.opengion.fukurou.model.ExcelModel 入力ファイル名" ); 759 return ; 760 } 761 762 ExcelModel model = new ExcelModel( args[0] , true ); 763 764 model.activeWorkbook(); // 余計な行を削除します。 765 766 StringBuilder buf = new StringBuilder( 200 ); 767 768 int shLen = model.getNumberOfSheets(); 769 for( int shNo=0; shNo<shLen; shNo++ ) { 770 String sheetName = model.getSheetName( shNo ); 771 772 int stRow = model.getFirstRowNum(); 773 int edRow = model.getLastRowNum(); 774 for( int rowNo=stRow; rowNo<=edRow; rowNo++ ) { 775 buf.setLength(0); // Clearの事 776 buf.append( sheetName ).append( '\t' ).append( rowNo ); 777 String[] vals = model.getValues( rowNo ); 778 if( vals != null ) { 779 for( int colNo=0; colNo<vals.length; colNo++ ) { 780 String val = vals[colNo] == null ? "" : vals[colNo]; 781 buf.append( '\t' ).append( val ); 782 } 783 } 784 System.out.println( buf ); 785 } 786 System.out.println(); 787 } 788 } 789}