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.File; // 6.2.0.0 (2015/02/27) 019import java.io.IOException; 020import java.io.OutputStream; 021import java.io.FileOutputStream; 022import java.io.BufferedOutputStream; 023import java.util.Locale; 024import java.util.Map; // 6.0.2.3 (2014/10/10) 画像関連 025import java.util.HashMap; // 6.0.2.3 (2014/10/10) 画像関連 026 027import org.apache.poi.common.usermodel.HyperlinkType; // 6.5.0.0 (2016/09/30) poi-3.15 028import org.apache.poi.ss.util.WorkbookUtil; 029import org.apache.poi.ss.usermodel.Workbook; 030import org.apache.poi.ss.usermodel.Sheet; 031import org.apache.poi.ss.usermodel.Row; 032import org.apache.poi.ss.usermodel.Cell; 033import org.apache.poi.ss.usermodel.CellType; // 6.5.0.0 (2016/09/30) poi-3.15 034import org.apache.poi.ss.usermodel.CellStyle; 035import org.apache.poi.ss.usermodel.VerticalAlignment; // 6.5.0.0 (2016/09/30) poi-3.15 036import org.apache.poi.ss.usermodel.BorderStyle; // 6.5.0.0 (2016/09/30) poi-3.15 037import org.apache.poi.ss.usermodel.Font; 038import org.apache.poi.ss.usermodel.IndexedColors; 039import org.apache.poi.ss.usermodel.RichTextString; 040import org.apache.poi.ss.usermodel.Hyperlink; 041import org.apache.poi.ss.usermodel.CreationHelper; 042import org.apache.poi.ss.usermodel.Drawing; // 6.0.2.3 (2014/10/10) 画像関連 043import org.apache.poi.ss.usermodel.ClientAnchor; // 6.0.2.3 (2014/10/10) 画像関連 044import org.apache.poi.ss.usermodel.Picture; // 6.0.2.3 (2014/10/10) 画像関連 045 046import org.apache.poi.hssf.usermodel.HSSFWorkbook; // .xls 047 048//import org.apache.poi.POIXMLDocumentPart; // 6.2.4.2 (2015/05/29) テキスト変換処理 049import org.apache.poi.ooxml.POIXMLDocumentPart; // 7.0.0.0 (2018/10/01) poi-ooxml-3.17.jar → poi-ooxml-4.0.0.jar 050 051import org.apache.poi.xssf.usermodel.XSSFDrawing; // 6.2.4.2 (2015/05/29) テキスト変換処理 052import org.apache.poi.xssf.usermodel.XSSFShape; // 6.2.4.2 (2015/05/29) テキスト変換処理 053import org.apache.poi.xssf.usermodel.XSSFSimpleShape; // 6.2.4.2 (2015/05/29) テキスト変換処理 054import org.apache.poi.xssf.usermodel.XSSFTextParagraph; // 6.2.4.2 (2015/05/29) テキスト変換処理 055import org.apache.poi.xssf.usermodel.XSSFTextRun; // 6.2.4.2 (2015/05/29) テキスト変換処理 056import org.apache.poi.xssf.streaming.SXSSFWorkbook; // .xlsx 6.3.7.0 (2015/09/04) 制限あり 高速、低メモリ消費 057 058import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 059import org.opengion.fukurou.system.Closer; 060import org.opengion.fukurou.util.ImageUtil; // 6.0.2.3 (2014/10/10) 画像関連 061 062import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 063import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 064 065/** 066 * POI による、EXCELバイナリファイルに対する、データモデルクラスです。 067 * 068 * 共通的な EXCEL処理 を集約しています。 069 * staticメソッドによる簡易的なアクセスの他に、順次処理も可能なように 070 * 現在アクセス中の、Workbook、Sheet、Row、Cell オブジェクトを内部で管理しています。 071 * 072 * 入力形式は、openXML形式にも対応しています。 073 * ファイルの内容に応じて、.xlsと.xlsxのどちらで読み取るかは、内部的に 074 * 自動判定されます。 075 * 076 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 077 * @og.group その他 078 * 079 * @version 6.0 080 * @author Kazuhiko Hasegawa 081 * @since JDK7.0, 082 */ 083public class ExcelModel { 084 /** このプログラムのVERSION文字列を設定します。 {@value} */ 085 private static final String VERSION = "6.8.2.4 (2017/11/20)" ; 086 087 private static final String DEF_SHEET_NAME = "Sheet" ; 088 089 // 6.0.2.3 (2014/10/10) ImageUtil の Suffix と、Workbook.PICTURE_TYPE_*** の関連付けをしておきます。 090 // Suffix 候補は、[bmp, gif, jpeg, jpg, png, wbmp] だが、対応する PICTURE_TYPE は一致しない。 091 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 092 private static final Map<String,Integer> PICTURE_TYPE ; 093 static { 094 PICTURE_TYPE = new HashMap<>() ; 095 PICTURE_TYPE.put( "png" , Integer.valueOf( Workbook.PICTURE_TYPE_PNG ) ); 096 PICTURE_TYPE.put( "jpeg" , Integer.valueOf( Workbook.PICTURE_TYPE_JPEG ) ); 097 PICTURE_TYPE.put( "jpg" , Integer.valueOf( Workbook.PICTURE_TYPE_JPEG ) ); 098 } 099 100 private final String inFilename ; // エラー発生時のキーとなる、EXCELファイル名 101 private final String sufix ; // 6.1.0.0 (2014/12/26) オープンしたファイル形式を記憶(ピリオドを含む) 102 103 private final Workbook wkbook ; // 現在処理中の Workbook 104 private Sheet sheet ; // 現在処理中の Sheet 105 private Row rowObj ; // 現在処理中の Row 106 107 private int refSheetIdx = -1; // 雛形シートのインデックス 108 109 private final CreationHelper createHelper ; // poi.xssf対応 110 111 private CellStyle style ; // 共通のセルスタイル 112 private CellStyle hLinkStyle ; // Hyperlink用のセルスタイル(青文字+下線) 113 114 private int maxColCount = 5 ; // 標準セル幅の5倍を最大幅とする。 115 private int dataStartRow = -1; // データ行の開始位置。未設定時は、-1 116 private boolean isAutoCellSize ; // カラム幅の自動調整を行うかどうか(true:行う/false:行わない) 117 118 private String addTitleSheet ; // Sheet一覧を先頭Sheetに作成する場合のSheet名 119 120 private String[] recalcSheetNames ; // 6.5.0.0 (2016/09/30) セルの計算式の再計算をさせるシート名の配列。 121 122 /** 123 * EXCELファイルのWookbookのデータ処理モデルを作成します。 124 * 125 * ここでは、既存のファイルを読み込んで、データ処理モデルを作成しますので、 126 * ファイルがオープンできなければエラーになります。 127 * 128 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 129 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 130 * 131 * @param file EXCELファイル 132 * @see #ExcelModel( File , boolean ) 133 */ 134 public ExcelModel( final File file ) { 135 this( file,true ); 136 } 137 138 /** 139 * EXCELファイルのWookbookのデータ処理モデルを作成します。 140 * 141 * isOpen条件によって、ファイルオープン(true)か、新規作成(false)が分かれます。 142 * ファイルオープンの場合は、EXCELの読み込み以外に、追記するとか、雛形参照する 143 * 場合にも、使用します。 144 * ファイルオープンの場合は、当然、ファイルがオープンできなければエラーになります。 145 * 146 * isOpen=新規作成(false) の場合は、ファイル名の拡張子で、XSSFWorkbook か HSSFWorkbook を 147 * 判定します。.xlsx の場合⇒XSSFWorkbook オブジェクトを使用します。 148 * 149 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 150 * @og.rev 6.0.2.3 (2014/10/10) POIUtil#createWorkbook( String ) を使用するように変更 151 * @og.rev 6.1.0.0 (2014/12/26) 入力ファイルの拡張子判定の対応 152 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 153 * @og.rev 6.2.2.0 (2015/03/27) マクロ付Excel(.xlsm)対応 154 * @og.rev 6.3.7.0 (2015/09/04),5.9.0.0 (2015/09/04) 標準を、SXSSFWorkbook に切り替えてみる。 155 * 156 * @param file EXCELファイル 157 * @param isOpen true:ファイルオープン/false:新規作成 158 * @see #ExcelModel( File ) 159 */ 160 public ExcelModel( final File file , final boolean isOpen ) { 161 inFilename = file.getName(); 162 163 final int idx = inFilename.lastIndexOf( '.' ); // 拡張子の位置 164 if( idx >= 0 ) { 165 sufix = inFilename.substring( idx ).toLowerCase( Locale.JAPAN ); // ピリオドを含む 166 } 167 else { 168 final String errMsg = "ファイルの拡張子が見当たりません。(.xls か .xlsx/.xlsm を指定下さい)" + CR 169 + " filename=[" + file + "]" + CR ; 170 throw new IllegalArgumentException( errMsg ); 171 } 172 173 if( isOpen ) { 174 wkbook = POIUtil.createWorkbook( file ); 175 } 176 else { 177 // 新規の場合、ファイル名に.xlsxで終了した場合⇒.xlsx形式ファイル作成、その他⇒.xls形式ファイル作成 178 if( ".xlsx".equals( sufix ) || ".xlsm".equals( sufix ) ) { // 6.2.2.0 (2015/03/27) 179 // 6.3.7.0 (2015/09/04),5.9.0.0 (2015/09/04) 標準を、SXSSFWorkbook に切り替えてみる。 180 // wkbook = new XSSFWorkbook(); 181 wkbook = new SXSSFWorkbook(); // 機能制限有:シートや行の削除や、AutoCellSize の指定ができないなど。 182 } 183 else if( ".xls".equals( sufix ) ) { 184 wkbook = new HSSFWorkbook(); 185 } 186 else { 187 final String errMsg = "ファイルの拡張子が不正です。(.xls か .xlsx/.xlsm のみ可能)" + CR 188 + " filename=[" + file + "]" + CR ; 189 throw new IllegalArgumentException( errMsg ); 190 } 191 } 192 193 createHelper = wkbook.getCreationHelper(); // poi.xssf対応 194 } 195 196 /** 197 * 内部 Workbook に、フォント名、フォントサイズを設定します。 198 * fontName(フォント名)は、"MS Pゴシック" など名称になります。 199 * fontPoint は、フォントの大きさを指定します。 200 * 内部的には、setFontHeightInPoints(short)メソッドで設定します。 201 * 202 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 203 * 204 * @param fontName フォント名 ("MS Pゴシック" など。nullの場合セットしません) 205 * @param fontPoint フォントの大きさ (0やマイナスの場合はセットしません) 206 */ 207 public void setFont( final String fontName , final short fontPoint ) { 208 // System.out.println( "FontName=" + fontName + " , Point=" + fontPoint ); 209 210 if( style == null ) { style = wkbook.createCellStyle(); } 211 212 final Font font = wkbook.createFont(); 213 // final Font font = wkbook.getFontAt( style.getFontIndex() ); // A,B などのヘッダーもフォントが 214 if( fontName != null ) { 215 font.setFontName( fontName ); // "MS Pゴシック" など 216 } 217 if( fontPoint > 0 ) { 218 font.setFontHeightInPoints( fontPoint ); 219 } 220 221 style.setFont( font ); 222 } 223 224 /** 225 * データ設定する セルに、罫線を追加します。 226 * 227 * ここで設定するのは、罫線の種類と、罫線の色ですが、内部的に固定にしています。 228 * Border=CellStyle.BORDER_THIN 229 * BorderColor=IndexedColors.BLACK 230 * 231 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 232 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX) 233 */ 234 public void setCellStyle() { 235 if( style == null ) { style = wkbook.createCellStyle(); } 236 237 // style.setBorderBottom( CellStyle.BORDER_THIN ); // 6.5.0.0 (2016/09/30) poi-3.12 238 // style.setBorderLeft( CellStyle.BORDER_THIN ); // 6.5.0.0 (2016/09/30) poi-3.12 239 // style.setBorderRight( CellStyle.BORDER_THIN ); // 6.5.0.0 (2016/09/30) poi-3.12 240 // style.setBorderTop( CellStyle.BORDER_THIN ); // 6.5.0.0 (2016/09/30) poi-3.12 241 242 style.setBorderBottom( BorderStyle.THIN ); // 6.4.6.0 (2016/05/27) poi-3.15 243 style.setBorderLeft( BorderStyle.THIN ); // 6.5.0.0 (2016/09/30) poi-3.15 244 style.setBorderRight( BorderStyle.THIN ); // 6.5.0.0 (2016/09/30) poi-3.15 245 style.setBorderTop( BorderStyle.THIN ); // 6.5.0.0 (2016/09/30) poi-3.15 246 247 style.setBottomBorderColor( IndexedColors.BLACK.getIndex() ); 248 style.setLeftBorderColor( IndexedColors.BLACK.getIndex() ); 249 style.setRightBorderColor( IndexedColors.BLACK.getIndex() ); 250 style.setTopBorderColor( IndexedColors.BLACK.getIndex() ); 251 252 // style.setVerticalAlignment( CellStyle.VERTICAL_TOP ); // isAutoCellSize=true 文字は上寄せする。 // 6.5.0.0 (2016/09/30) poi-3.12 253 style.setVerticalAlignment( VerticalAlignment.TOP ); // isAutoCellSize=true 文字は上寄せする。 // 6.5.0.0 (2016/09/30) poi-3.15 254 // style.setWrapText( true ); // isAutoCellSize=true 折り返して表示する。 255 } 256 257 /** 258 * 全てのSheetに対して、autoSizeColumn設定を行うかどうか指定します(初期値:false)。 259 * 260 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 261 * 初期カラム幅の5倍を限度にしています。 262 * 263 * なお、autoSizeColumn設定は負荷の大きな処理なので、saveFile(String)の 264 * 中で実行されます。(セーブしなければ実行されません。) 265 * よって、指定は、いつ行っても構いません。 266 * 267 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 268 * 269 * @param flag autoSizeColumn設定を行うかどうか [true:自動カラム幅設定を行う/false:行わない] 270 * @see #useAutoCellSize( boolean,int ) 271 */ 272 public void useAutoCellSize( final boolean flag ) { 273 isAutoCellSize = flag; 274 } 275 276 /** 277 * 全てのSheetに対して、autoSizeColumn設定を行うかどうか指定します(初期値:false)。 278 * 279 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 280 * 初期カラム幅のcount倍を限度に設定します。 281 * ただし、count がマイナスの場合は、無制限になります。 282 * 283 * なお、autoSizeColumn設定は負荷の大きな処理なので、saveFile(String)の 284 * 中で実行されます。(セーブしなければ実行されません。) 285 * よって、指定は、いつ行っても構いません。 286 * 287 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 288 * 289 * @param flag autoSizeColumn設定を行うかどうか [true:自動カラム幅設定を行う/false:行わない] 290 * @param count 最大幅を標準セル幅の何倍にするかを指定。マイナスの場合は、無制限 291 * @see #useAutoCellSize( boolean ) 292 */ 293 public void useAutoCellSize( final boolean flag, final int count ) { 294 isAutoCellSize = flag; 295 maxColCount = count ; 296 } 297 298 /** 299 * EXCELで、出力処理の最後にセルの計算式の再計算をさせるシート名の配列を指定します。 300 * 301 * null の場合は、再計算しません。 302 * なお、再計算は、saveFile(String)の中で実行されます。(セーブしなければ実行されません。) 303 * 304 * @og.rev 6.5.0.0 (2016/09/30) セルの計算式の再計算をさせる recalcSheetNames 属性の追加。 305 * 306 * @param sheets 対象シート名の配列 307 */ 308 public void setRecalcSheetName( final String[] sheets ){ 309 recalcSheetNames = sheets; 310 } 311 312 /** 313 * データ行の書き込み開始位置の行番号を設定します。 314 * 315 * これは、autoSize設定で、自動調整するカラムを、ヘッダーではなく、 316 * データ部で計算する場合に使用します。 317 * 318 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 319 * 320 * @param st データ行の開始位置。未設定時は、-1 321 * @see #useAutoCellSize( boolean ) 322 */ 323 public void setDataStartRow( final int st ) { 324 dataStartRow = st; 325 } 326 327 /** 328 * Sheet一覧を先頭Sheetに作成する場合のSheet名を指定します。 329 * 330 * これは、Workbook に含まれる Sheet 一覧を作成する場合に、利用可能です。 331 * 332 * この処理は、#saveFile( File ) 処理時に、実行されます。 333 * 334 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 335 * 336 * @param shtName Sheet一覧のSheet名 337 * @see #makeAddTitleSheet() 338 */ 339 public void setAddTitleSheet( final String shtName ) { 340 addTitleSheet = shtName; 341 } 342 343 /** 344 * 内部 Workbookの Sheet数を返します。 345 * 346 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 347 * 348 * @return シート数 349 */ 350 public int getNumberOfSheets() { 351 return wkbook.getNumberOfSheets(); 352 } 353 354 /** 355 * 内部 Workbookより、雛形Sheetをセットします。 356 * 357 * これは、雛形シートを使用する場合に、使います。このメソッドが呼ばれると、 358 * 雛形シートを使用すると判定されます。 359 * 雛形シート名が、内部 Workbook に存在しない場合は、エラーになります。 360 * ただし、null をセットした場合は、最初のシートを雛形シートとして使用すると 361 * 判定します。 362 * 363 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 364 * 365 * @param refSheetName 参照シート名(nullの場合、参照シート使用する場合は、先頭のシート) 366 */ 367 public void setRefSheetName( final String refSheetName ) { 368 // 参照シート名の指定がない場合は、最初のシート 369 refSheetIdx = ( refSheetName == null ) ? 0 : wkbook.getSheetIndex( refSheetName ); 370 371 if( refSheetIdx < 0 ) { // 参照シート名が存在しなかった。 372 final String errMsg = "指定の参照シート名は存在しませんでした。" + CR 373 + " inFilename=[" + inFilename + "] , refSheetName=[" + refSheetName + "]" + CR ; 374 throw new IllegalArgumentException( errMsg ); 375 } 376 } 377 378 /** 379 * 内部 Workbookより、新しいSheetを作ります。 380 * 381 * 先に雛形シートを指定している場合は、その雛形シートから作成します。 382 * 指定していない場合は、新しいシートを作成します。 383 * 雛形シートを参照する場合は、雛形シートそのものを返します。 384 * また、雛形シートの枚数を超える場合は、前の雛形シートをコピーします。 385 * 雛形シートが存在しない場合は、新しいシートを作成します。 386 * 387 * シート名は、重複チェックを行い、同じ名前のシートの場合は、(1),(2)が付けられます。 388 * shtName が null の場合は、"Sheet" が割り振られます。 389 * 390 * この処理を行うと、内部の Sheet にも、ここで作成された Sheet が設定されます。 391 * 392 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 393 * @og.rev 6.2.2.3 (2015/04/10) 雛形シートにそのままデータを書き込んでいく。 394 * @og.rev 6.5.0.0 (2016/09/30) 雛形シート名をそのまま使用する場合は、isOverwrite に、true を指定します。 395 * 396 * @param shtName シート名 (重複する場合は、(2)、(3)のような文字列を追加 、nullの場合は、"Sheet") 397 * @param isOverwrite 雛形シート名をそのまま使用する場合は、true を指定します。 398 */ 399 public void createSheet( final String shtName , final boolean isOverwrite ) { 400 // 参照シートを使う場合(整合性の問題で、両方ともチェックしておきます) 401 402 // 6.2.2.3 (2015/04/10) 雛形シートにそのままデータを書き込んでいく。 403 final int shtNo ; 404 if( refSheetIdx < 0 ) { // 雛形シートを使用しない。 405 sheet = wkbook.createSheet(); 406 shtNo = wkbook.getNumberOfSheets() - 1; 407 } 408 else if( refSheetIdx >= wkbook.getNumberOfSheets() ) { // シート数が雛形より超えている。 409 sheet = wkbook.cloneSheet( refSheetIdx-1 ); // 最後の雛形シートをコピーします。 410 shtNo = wkbook.getNumberOfSheets() - 1; 411 refSheetIdx++ ; 412 } 413 else { 414 sheet = wkbook.getSheetAt( refSheetIdx ); // 雛形シートをそのまま使用 415 shtNo = refSheetIdx; 416 refSheetIdx++ ; 417 } 418 419 // 6.5.0.0 (2016/09/30) 雛形シート名をそのまま使用する場合。 420 if( !isOverwrite ) { 421 setSheetName( shtNo , shtName ); 422 } 423 } 424 425 /** 426 * 内部 Workbook の指定のシート番号の Sheet の名前を設定します。 427 * 428 * 指定のシート名が、既存のシートになければ、そのまま設定します。 429 * すでに、同じ名前のシートが存在する場合は、そのシート名の後に 430 * (1)、(2)、(3)のような文字列を追加します。 431 * shtName が null の場合は、"Sheet" が割り振られます。 432 * 433 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 434 * @og.rev 6.2.5.1 (2015/06/12) シート名重複が自分自身の場合は、(1)等の追加は行わない。 435 * 436 * @param shtNo シート番号 437 * @param shtName シート名 (重複する場合は、(1)、(2)のような文字列を追加 、nullの場合は、"Sheet") 438 */ 439 public void setSheetName( final int shtNo, final String shtName ) { 440 String tempName = ( shtName == null ) ? DEF_SHEET_NAME : WorkbookUtil.createSafeSheetName( shtName ) ; 441 int cnt = 1; 442 443 // 6.2.5.1 (2015/06/12) シート名重複が自分自身の場合は、(1)等の追加は行わない。 444 // ※ EXCELのシート名は、大文字、小文字だけでなく、全角半角の区別もしない。 445 final String nowName = wkbook.getSheetName( shtNo ); 446 if( tempName != null && !tempName.equals( nowName ) ) { // 全く同一の場合は、何もしない。 447 if( shtNo == wkbook.getSheetIndex( tempName ) ) { // シート名判定が、自身の場合 448 wkbook.setSheetName( shtNo,tempName ); 449 } 450 else { 451 while( wkbook.getSheetIndex( tempName ) >= 0 ) { // シート名が存在している場合 452 tempName = WorkbookUtil.createSafeSheetName( shtName + "(" + cnt + ")" ); 453 if( tempName.length() >= 31 ) { // 重複時の追加文字分を減らす。 454 tempName = tempName.substring( 0,26 ) + "(" + cnt + ")" ; // cnt3桁まで可能 455 } 456 cnt++; 457 } 458 wkbook.setSheetName( shtNo,tempName ); 459 } 460 } 461 } 462 463 /** 464 * 内部 Workbook の 指定のSheet番号のシート名前を返します。 465 * 466 * シートが存在しない場合は、null を返します。 467 * 468 * この処理を行うと、内部の Sheet にも、ここで見つけた Sheet が設定されます。 469 * 470 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 471 * 472 * @param shtNo シート番号 473 * 474 * @return shtName シート名 475 */ 476 public String getSheetName( final int shtNo ) { 477 final int shLen = wkbook.getNumberOfSheets(); 478 479 String shtName = null; 480 if( shtNo < shLen ) { 481 sheet = wkbook.getSheetAt( shtNo ); // 現在の sheet に設定する。 482 shtName = sheet.getSheetName(); 483 } 484 485 return shtName ; 486 } 487 488 /** 489 * 内部 Workbook の 指定のSheet名のシート番号を返します。 490 * 491 * シートが存在しない場合は、-1 を返します。 492 * この処理を行うと、内部の Sheet にも、ここで見つけた Sheet が設定されます。 493 * シートが存在しない場合、内部の Sheet オブジェクトも null がセットされますのでご注意ください。 494 * 495 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 496 * 497 * @param shtName シート名 498 * 499 * @return シート番号(名前のシートがなければ、-1) 500 */ 501 public int getSheetNo( final String shtName ) { 502 sheet = wkbook.getSheet( shtName ); // シート名がマッチしなければ、null 503 504 return wkbook.getSheetIndex( shtName ) ; // シート名がマッチしなければ、-1 505 } 506 507 /** 508 * Excelの指定Sheetオブジェクトを削除します。 509 * 510 * 削除するシートは、シート番号でFrom-To形式で指定します。 511 * Fromも Toも、削除するシート番号を含みます。 512 * 例えば、0,3 と指定すると、0,1,2,3 の 4シート分を削除します。 513 * 514 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 515 * 516 * @param fromNo 削除する開始シート番号(含む) 517 * @param toNo 削除する終了シート番号(含む) 518 */ 519 public void removeSheet( final int fromNo,final int toNo ) { 520 for( int shtNo=toNo; shtNo>=fromNo; shtNo-- ) { // 逆順に処理します。 521 wkbook.removeSheetAt( shtNo ); 522 } 523 } 524 525 /** 526 * 内部 Workbookの 現在Sheet の最初の行番号を返します。 527 * 528 * 行は、0 から始まります。 529 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 530 * 531 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 532 * 533 * @return 最初の行番号 534 */ 535 public int getFirstRowNum() { 536 return sheet.getFirstRowNum(); 537 } 538 539 /** 540 * 内部 Workbookの 現在Sheet の最後の行番号を返します。 541 * 542 * 最終行は、含みます。よって、行数は、getLastRowNum()+1になります。 543 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 544 * 545 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 546 * 547 * @return 最後の行番号 548 */ 549 public int getLastRowNum() { 550 return sheet.getLastRowNum(); 551 } 552 553 /** 554 * Excelの指定行のRowオブジェクトを作成します。 555 * 556 * 指定行の Row オブジェクトが存在しない場合は、新規作成します。 557 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 558 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 559 * 560 * この処理を行うと、内部の Rowオブジェクトが設定されます。 561 * 562 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 563 * 564 * @param rowNo 行の番号 565 */ 566 public void createRow( final int rowNo ) { 567 rowObj = sheet.getRow( rowNo ); 568 if( rowObj == null ) { rowObj = sheet.createRow( rowNo ); } 569 } 570 571 /** 572 * Excelの指定行以降の余計なRowオブジェクトを削除します。 573 * 574 * 指定行の Row オブジェクトから、getLastRowNum() までの行を、削除します。 575 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 576 * 577 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 578 * 579 * @param startRowNum 指定以降の余計な行を削除 580 */ 581 public void removeRow( final int startRowNum ) { 582 final int stR = startRowNum; 583 final int edR = sheet.getLastRowNum(); 584 585 for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 586 final Row rowObj = sheet.getRow( rowNo ); 587 if( rowObj != null ) { sheet.removeRow( rowObj ); } 588 } 589 } 590 591 /** 592 * Excelの処理中のRowオブジェクトの指定カラム以降の余計なCellオブジェクトを削除します。 593 * 594 * 指定行の Row オブジェクトから、getLastCellNum() までのカラムを、削除します。 595 * この処理は、内部Rowが作成されているか、null でない場合のみ実行できます。 596 * 597 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 598 * 599 * @param startCellNum 指定以降の余計なカラムを削除 600 */ 601 public void removeCell( final int startCellNum ) { 602 final int stC = startCellNum; 603 final int edC = rowObj.getLastCellNum(); 604 605 for( int colNo=edC; colNo>=stC; colNo-- ) { // 逆順に処理します。 606 final Cell colObj = rowObj.getCell( colNo ); 607 if( colObj != null ) { rowObj.removeCell( colObj ); } 608 } 609 } 610 611 /** 612 * row にあるセルのオブジェクト値を設定します。 613 * 614 * 行が存在しない場合、行を追加します。 615 * この処理を行うと、内部の Rowオブジェクトがなければ新規作成されます。 616 * 617 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 618 * 619 * @param vals 新しい配列値。 620 * @param rowNo 値が変更される行(無視されます) 621 */ 622 public void setValues( final String[] vals,final int rowNo ) { 623 if( rowObj == null ) { createRow( rowNo ); } 624 625 if( vals != null ) { 626 for( int colNo=0; colNo<vals.length; colNo++ ) { 627 setCellValue( vals[colNo],colNo ); 628 } 629 } 630 } 631 632 /** 633 * row にあるセルのオブジェクト値を設定します。 634 * 635 * 行が存在しない場合、行を追加します。 636 * 引数に、カラムがNUMBER型かどうかを指定することが出来ます。 637 * この処理を行うと、内部の Rowオブジェクトがなければ新規作成されます。 638 * 639 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 640 * 641 * @param vals 新しい配列値。 642 * @param rowNo 値が変更される行(無視されます) 643 * @param isNums セルが、NUMBER型の場合は、true/それ以外は、false 644 */ 645 public void setValues( final String[] vals,final int rowNo,final boolean[] isNums ) { 646 if( rowObj == null ) { createRow( rowNo ); } 647 648 if( vals != null ) { 649 for( int colNo=0; colNo<vals.length; colNo++ ) { 650 setCellValue( vals[colNo],colNo,isNums[colNo] ); 651 } 652 } 653 } 654 655 /** 656 * Excelの指定セルにデータを設定します。 657 * 658 * ここで設定する行は、現在の内部 Row です。 659 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 660 * このメソッドでは、データを文字列型として設定します。 661 * この処理は、内部Rowが作成されているか、null でない場合のみ実行できます。 662 * 663 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 664 * 665 * @param dataVal String文字列 666 * @param colNo セルの番号(0,1,2・・・・) 667 * @see #setCellValue( String,int,boolean ) 668 */ 669 public void setCellValue( final String dataVal , final int colNo ) { 670 setCellValue( dataVal,colNo,false ); 671 } 672 673 /** 674 * Excelの指定セルにデータを設定します。 675 * 676 * ここで設定する行は、現在の内部 Row です。 677 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 678 * このメソッドでは、引数のデータ型をNUMBER型の場合は、doubleに変換して、 679 * それ以外は文字列としてとして設定します。 680 * この処理は、内部Rowが作成されているか、null でない場合のみ実行できます。 681 * 682 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 683 * 684 * @param dataVal String文字列 685 * @param colNo セルの番号(0,1,2・・・・) 686 * @param isNumber セルが、NUMBER型の場合は、true/それ以外は、false 687 * @see #createRow( int ) 688 * @see #setCellValue( String,int ) 689 */ 690 public void setCellValue( final String dataVal , final int colNo , final boolean isNumber ) { 691 Cell colObj = rowObj.getCell( colNo ); 692 if( colObj == null ) { colObj = rowObj.createCell( colNo ); } 693 694 if( style != null ) { colObj.setCellStyle(style); } 695 696 // CELL_TYPE_NUMERIC 以外は、String扱いします。 697 if( isNumber ) { 698 final Double dbl = parseDouble( dataVal ); 699 if( dbl != null ) { 700 colObj.setCellValue( dbl.doubleValue() ); 701 return ; // Double 変換できた場合は、即抜けます。 702 } 703 } 704 705 final RichTextString richText = createHelper.createRichTextString( dataVal ); 706 colObj.setCellValue( richText ); 707 } 708 709 /** 710 * Excelの指定セルにHyperlinkを設定します。 711 * 712 * ここで設定する行は、現在の内部 Row です。 713 * Row を切り替えたい場合は、#createRow( int ) を呼び出してください。 714 * このメソッドで設定するHyperlinkは、Sheetに対する LINK_DOCUMENT です。 715 * 先に、セルに対する値をセットしておいてください。 716 * Hyperlinkは、文字に対して、下線 と 青字 のスタイル設定を行います。 717 * 718 * Link文字列(シート名) が、null や ゼロ文字列の場合は、処理を行いません。 719 * 720 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 721 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Hyperlink.LINK_XXXX → HyperlinkType.XXXX) 722 * 723 * @param linkVal Link文字列(シート名) 724 * @param colNo セルの番号(0,1,2・・・・) 725 * @see #setCellValue( String,int ) 726 */ 727 public void setCellLink( final String linkVal , final int colNo ) { 728 if( linkVal == null || linkVal.isEmpty() ) { return; } 729 730 Cell colObj = rowObj.getCell( colNo ); 731 if( colObj == null ) { colObj = rowObj.createCell( colNo ); } 732 733 if( hLinkStyle == null ) { 734 hLinkStyle = wkbook.createCellStyle(); 735 if( style != null ) { hLinkStyle.cloneStyleFrom(style); } 736 737 final Font font = wkbook.createFont(); 738 font.setColor( IndexedColors.BLUE.getIndex() ); // リンクは青文字 739 font.setUnderline( Font.U_SINGLE ); // 下線付 740 741 hLinkStyle.setFont( font ); 742 } 743 colObj.setCellStyle(hLinkStyle); 744 745 // final Hyperlink hLink = createHelper.createHyperlink( Hyperlink.LINK_DOCUMENT ); // 6.5.0.0 (2016/09/30) poi-3.12 746 final Hyperlink hLink = createHelper.createHyperlink( HyperlinkType.DOCUMENT ); // 6.5.0.0 (2016/09/30) poi-3.15 747 hLink.setAddress( "'" + linkVal + "'!A1" ); 748 colObj.setHyperlink( hLink ); 749 } 750 751 /** 752 * 現在のRow にあるセルの属性値を配列で返します。 753 * 754 * Rowオブジェクトが存在しない場合は、長さ0の配列を返します。 755 * また、Rowオブジェクトの中の セルオブジェクトが存在しない場合は、 756 * null がセットされます。 757 * 758 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 759 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 760 * 761 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 762 * @og.rev 6.3.9.0 (2015/11/06) ExcelModel#getValues(int) では、nullは返さない。 763 * @og.rev 6.3.9.1 (2015/11/27) メソッドの出口は、最後の1か所にすべきです(PMD)。 764 * 765 * @param rowNo 行の番号 766 * @return 指定されたセルの属性値。Rowがnullの場合は、長さ0の配列を返します。 767 * @og.rtnNotNull 768 */ 769 public String[] getValues( final int rowNo ) { 770 rowObj = sheet.getRow( rowNo ); 771 772 final int len = rowObj == null ? 0 : rowObj.getLastCellNum(); // 含まないので、length と同じ意味になる。 773 final String[] vals = new String[len]; // 6.3.9.1 (2015/11/27) メソッドの出口 774 775 for( int colNo=0; colNo<len; colNo++ ) { 776 final Cell colObj = rowObj.getCell( colNo ); 777 vals[colNo] = POIUtil.getValue( colObj ); 778 } 779 780 return vals ; 781 } 782 783 /** 784 * 現在のrow にあるセルの属性値を返します。 785 * 786 * セルオブジェクトが存在しない場合は、null を返します。 787 * 788 * この処理は、内部Sheetが作成されているか、null でない場合のみ実行できます。 789 * この処理を実行すると、指定行の Rowオブジェクトが内部 Row に設定されます。 790 * 791 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 792 * @og.rev 6.3.9.1 (2015/11/27) メソッドの出口は、最後の1か所にすべきです(PMD)。 793 * 794 * @param rowNo 値が参照される行 795 * @param colNo 値が参照される列 796 * 797 * @return 指定されたセルの値 T 798 */ 799 public String getValue( final int rowNo, final int colNo ) { 800 rowObj = sheet.getRow( rowNo ); 801 802 return rowObj == null ? null : POIUtil.getValue( rowObj.getCell( colNo ) ); 803 } 804 805 /** 806 * 指定のシートの行・列の箇所に、イメージファイルを挿入します。 807 * 808 * ここでは、セル範囲ではなく、指定の行列の箇所に、アンカーを設定して、画像ファイルを 809 * 挿入します。一応、リサイズして、元の大きさ近くに戻しますが、縦横比が変わってしまいます。 810 * 正確に挿入する場合は、セル範囲の指定と、マージンを指定しなければなりませんが、 811 * 微調整が必要です。 812 * 813 * この処理で使用される Sheetオブジェクトは一時的に作成されます。(キャッシュされません) 814 * 一連処理のどのタイミングで実行しても、内部の状態には影響はありません。 815 * 816 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 817 * 818 * @param imgFile 挿入するイメージファイル名 819 * @param shtNo シート番号 820 * @param rowNo 挿入する行 821 * @param colNo 挿入する列 822 */ 823 public void addImageFile( final String imgFile, final int shtNo, final int rowNo, final int colNo ) { 824 addImageFile( imgFile,shtNo,rowNo,colNo,rowNo,colNo,0,0,0,0 ); 825 } 826 827 /** 828 * 指定のシートの行・列の箇所に、イメージファイルを挿入します。 829 * 830 * ここでは、セル範囲ではなく、指定の行列の箇所に、アンカーを設定して、画像ファイルを 831 * 挿入します。一応、リサイズして、元の大きさ近くに戻しますが、縦横比が変わってしまいます。 832 * 正確に挿入する場合は、セル範囲の指定と、マージンを指定しなければなりませんが、 833 * 微調整が必要です。 834 * 835 * この処理で使用される Sheetオブジェクトは一時的に作成されます。(キャッシュされません) 836 * 一連処理のどのタイミングで実行しても、内部の状態には影響はありません。 837 * 838 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 839 * @og.rev 6.4.6.0 (2016/05/27) poi-3.15 準備 840 * @og.rev 6.8.2.4 (2017/11/20) poi-3.17 で、警告: [rawtypes] raw型が見つかりました対応 841 * 842 * @param imgFile 挿入するイメージファイル名 843 * @param shtNo シート番号 844 * @param row1 挿入する行(開始) 845 * @param col1 挿入する列(開始) 846 * @param row2 挿入する行(終了-含まず) 847 * @param col2 挿入する列(終了-含まず) 848 * @param dx1 開始セルのX軸座標(マージン) 849 * @param dy1 開始セルのY軸座標(マージン) 850 * @param dx2 終了セルのX軸座標(マージン) 851 * @param dy2 終了セルのY軸座標(マージン) 852 */ 853 public void addImageFile( final String imgFile , final int shtNo , 854 final int row1 , final int col1 , final int row2 , final int col2 , 855 final int dx1 , final int dy1 , final int dx2 , final int dy2 ) { 856 final String suffix = ImageUtil.getSuffix( imgFile ); 857 final Integer picType = PICTURE_TYPE.get( suffix ); 858 859 // 実験した結果、bmp,gif,tif については、PICTURE_TYPE_PNG で、挿入できた。 860 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 861 final int pictureType = picType == null ? Workbook.PICTURE_TYPE_PNG : picType.intValue() ; 862 863 final byte[] imgs = ImageUtil.byteImage( imgFile ); 864 865 final int pictureIdx = wkbook.addPicture( imgs, pictureType ); 866 867 final Sheet sheet = wkbook.getSheetAt( shtNo ); 868 // 6.8.2.4 (2017/11/20) poi-3.17 で、警告: [rawtypes] raw型が見つかりました対応 869 final Drawing<?> patriarch = sheet.createDrawingPatriarch(); // 昔は一度しか実行できなかったようです。 870 // final Drawing patriarch = sheet.createDrawingPatriarch(); // 昔は一度しか実行できなかったようです。 871 872 final ClientAnchor anchor = patriarch.createAnchor( dx1,dy1,dx2,dy2,col1,row1,col2,row2 ); 873 // ClientAnchor anchor = createHelper.createClientAnchor(); でも作成可能。 874 875 // MOVE_AND_RESIZE, MOVE_DONT_RESIZE, DONT_MOVE_AND_RESIZE から、決め打ち。 876 // anchor.setAnchorType( ClientAnchor.MOVE_DONT_RESIZE ); // 6.4.6.0 (2016/05/27) poi-3.12 877 anchor.setAnchorType( ClientAnchor.AnchorType.MOVE_DONT_RESIZE ); // 6.4.6.0 (2016/05/27) poi-3.15 878 879 final Picture pic = patriarch.createPicture( anchor, pictureIdx ); 880 // セルの範囲指定がゼロの場合、画像サイズもゼロになる為、リサイズしておく。 881 if( row1 == row2 || col1 == col2 ) { pic.resize(); } // resize すると、anchor のマージンが無視されるようです。 882 } 883 884 /** 885 * 内部 Workbook オブジェクトをファイルに書き出します。 886 * 887 * Excelの形式は、ここで指定する出力ファイルの拡張子ではなく、コンストラクタで 888 * 指定したファイルの拡張子で決まります。 889 * 異なる形式の拡張子を持つファイルを指定した場合、強制的に、オープンした 890 * Workbook の形式の拡張子を追加します。 891 * 892 * 拡張子は、Excel 2007以降の形式(.xlsx)か、Excel 2003以前の形式(.xls) が指定できます。 893 * 拡張子が未設定の場合は、オープンした Workbook の形式に合わせた拡張子を付与します。 894 * 895 * isAutoCellSize=true の場合は、ここで全Sheetに対してCell幅の自動調整が行われます。 896 * 897 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 898 * @og.rev 6.1.0.0 (2014/12/26) 入力ファイルの拡張子判定の対応 899 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 900 * @og.rev 6.5.0.0 (2016/09/30) セルの計算式の再計算をさせる recalcSheetNames 属性の追加。 901 * 902 * @param file セーブするファイル 903 */ 904 public void saveFile( final File file ) { 905 final File saveFile ; 906 String fname = file.getName(); 907 if( fname.toLowerCase(Locale.JAPAN).endsWith( sufix ) ) { 908 saveFile = file; 909 } 910 else { 911 final int idx = fname.lastIndexOf( '.' ); 912 if( idx >= 0 ) { fname = fname.substring( 0,idx ); } 913 saveFile = new File( file.getParent() , fname + sufix ); 914 } 915 916 if( isAutoCellSize ) { POIUtil.autoCellSize( wkbook, maxColCount, dataStartRow ); } 917 918 // 6.5.0.0 (2016/09/30) セルの計算式の再計算をさせる recalcSheetNames 属性の追加。 919 if( recalcSheetNames != null && recalcSheetNames.length > 0 ) { 920 for( final String shtName : recalcSheetNames ) { 921 final Sheet sht = wkbook.getSheet( shtName ); // シート名がマッチしなければ、null 922 if( sht != null ) { sht.setForceFormulaRecalculation(true); } 923 } 924 } 925 926 // こちらの都合で、TitleSheet は、autoCellSize ではなく、Sheet#autoSizeColumn(int) を使用して、自動計算させる。 927 if( addTitleSheet != null ) { makeAddTitleSheet(); } 928 929 OutputStream fileOut = null ; 930 try { 931 fileOut = new BufferedOutputStream( new FileOutputStream( saveFile ) ); // 6.1.0.0 (2014/12/26) 932 wkbook.write( fileOut ); 933 } 934 catch( final IOException ex ) { 935 final String errMsg = "ファイルへ書込み中にエラーが発生しました。" + CR 936 + " File=" + saveFile + CR 937 + ex.getMessage() ; 938 throw new OgRuntimeException( errMsg,ex ); 939 } 940 finally { 941 Closer.ioClose( fileOut ); 942 } 943 } 944 945 /** 946 * 内部 Workbook オブジェクトのSheet一覧のSheetを、先頭に追加します。 947 * 948 * これは、Workbook に含まれる Sheet 一覧を作成する場合に、利用可能です。 949 * 950 * この処理は、内部のWorkbook、Sheetオブジェクトに依存して実行されます。 951 * また、単独ではなく、#saveFile( File ) 実行時に、addTitleSheet が 952 * 設定されている場合のみ、実行されます。 953 * 954 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 955 * 956 * @see #saveFile( File ) 957 * @see #setAddTitleSheet( String ) 958 */ 959 private void makeAddTitleSheet() { 960 sheet = wkbook.createSheet(); 961 final String shtNm = sheet.getSheetName(); // Sheet名の取得 962 wkbook.setSheetOrder( shtNm,0 ); // そのSheetを先頭に移動 963 setSheetName( 0,addTitleSheet ); // そのSheet名を変更 → これが、TitleSheet 964 965 int rowNo = 0; 966 createRow( rowNo++ ); // 先頭行(インスタンス共通のRowオブジェクト)作成 967 setCellValue( "No" , 0 ); 968 setCellValue( "Sheet", 1 ); 969 970 final int shCnt = wkbook.getNumberOfSheets(); 971 for( int shtNo=1; shtNo<shCnt; shtNo++,rowNo++ ) { 972 final String nm = wkbook.getSheetName( shtNo ); 973 974 createRow( rowNo ); // 行の追加作成 975 setCellValue( String.valueOf( rowNo ),0,true ); // 行番号として、数字型で登録 976 setCellValue( nm , 1 ); // シートの値を書き込む 977 setCellLink( nm , 1 ); // シートへのリンクを作成する。 978 } 979 980 sheet.autoSizeColumn( 0 ); 981 sheet.autoSizeColumn( 1 ); 982 } 983 984 /** 985 * Workbook の全Sheetを対象に、空行を取り除き、全体をシュリンクします。 986 * 987 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 988 * 989 * ここでは、Row を逆順にスキャンし、Cellが 存在しない間は、行を削除します。 990 * 途中の空行の削除ではなく、最終行からの連続した空行の削除です。 991 * 992 * isCellDel=true を指定すると、Cellの末尾削除を行います。 993 * 有効行の最後のCellから空セルを削除していきます。 994 * 表形式などの場合は、Cellのあるなしで、レイアウトが崩れる場合がありますので 995 * 処理が不要な場合は、isCellDel=false を指定してください。 996 * 997 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 998 * 999 * @param isCellDel Cellの末尾削除を行うかどうか(true:行う/false:行わない) 1000 */ 1001 public void activeWorkbook( final boolean isCellDel ) { 1002 POIUtil.activeWorkbook( wkbook, isCellDel ); 1003 } 1004 1005 /** 1006 * Workbook の全Sheetを対象に、テキスト変換処理を行います(XSLX限定)。 1007 * 1008 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 1009 * #activeWorkbook( boolean ) との順番は構いません。 1010 * 1011 * ・シート名の一覧をピックアップします。 1012 * ・セル値を、セル単位にピックアップします。 1013 * ・オブジェクト文字列を、改行単位にピックアップし、結果を合成します。 1014 * 1015 * ここでは、内部的に、TextConverterインターフェースを作成して処理します。 1016 * 1017 * @og.rev 6.2.4.2 (2015/05/29) テキスト変換処理 1018 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 1019 * @og.rev 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1020 * 1021 * @param convMap 変換対象を管理するMapオブジェクト 1022 * @see #textConverter( TextConverter ) 1023 */ 1024 public void textConverter( final Map<String,String> convMap ) { 1025 textConverter( 1026 ( val,cmnt ) -> convMap.get( val ) 1027 ); 1028 1029 // textConverter( 1030 // new TextConverter<String,String>() { 1031 // /** 1032 // * 入力文字列を、変換します。 1033 // * 1034 // * @param val 入力文字列 1035 // * @param cmnt コメント 1036 // * @return 変換文字列(変換されない場合は、null) 1037 // */ 1038 // @Override 1039 // public String change( final String val , final String cmnt ) { 1040 // return convMap.get( val ); 1041 // } 1042 // } 1043 // ); 1044 } 1045 1046 /** 1047 * Workbook の全Sheetを対象に、テキスト変換処理を行います(XSLX限定)。 1048 * 1049 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 1050 * #activeWorkbook( boolean ) との順番は構いません。 1051 * 1052 * ・シート名の一覧をピックアップします。 1053 * ・セル値を、セル単位内の改行単位にピックアップし、結果を合成ます。 1054 * ・オブジェクト文字列を、改行単位にピックアップし、結果を合成します。 1055 * 1056 * ここでは、シート名、セルテキスト、SimpleShapeオブジェクトのテキストを 1057 * input に、TextConverterインターフェース の change メソッドを呼び出します。 1058 * 戻り値が、null でないなら、元のデータと置き換えます。 1059 * 戻り値が、null の場合は、そのまま読み飛ばします。(なにもしません) 1060 * EXCELへの書き戻しが発生しますので、万一、ファイル破損で、開けなくなる場合を 1061 * 想定して、バックアップファイルは、各自で準備してください。 1062 * 1063 * @og.rev 6.2.4.2 (2015/05/29) テキスト変換処理 1064 * @og.rev 6.2.5.0 (2015/06/05) xsl形式のオブジェクト取得…はできなかった。 1065 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 1066 * @og.rev 6.3.9.0 (2015/11/06) セルに値をセットするときに、セルタイプを考慮する。 1067 * 1068 * @param conv TextConverterインターフェース 1069 * @see #textConverter( Map ) 1070 */ 1071 @SuppressWarnings(value={"deprecation"}) // poi-3.15 1072 public void textConverter( final TextConverter<String,String> conv ) { 1073 // if( ".xlsx".equals( sufix ) || ".xlsm".equals( sufix ) ) { 1074 final int shCnt = wkbook.getNumberOfSheets(); 1075 for( int shtNo=0; shtNo<shCnt; shtNo++ ) { 1076 final Sheet sht = wkbook.getSheetAt( shtNo ); 1077 // シート名の変換 1078 final String shtNm = conv.change( sht.getSheetName() , "Sheet" ); 1079 if( shtNm != null ) { 1080 setSheetName( shtNo,shtNm ); // 同一シート対策済みのメソッドを呼び出す。 1081 } 1082 1083 // セル値の変換 1084 final int stR = Math.max( sht.getFirstRowNum(),0 ); // stR が、マイナスのケースがある。 1085 final int edR = sht.getLastRowNum(); 1086 1087 for( int rowNo=stR; rowNo<=edR; rowNo++ ) { 1088 final Row rowObj = sht.getRow( rowNo ); 1089 if( rowObj != null ) { 1090 final int stC = Math.max( rowObj.getFirstCellNum(),0 ); // stC が、マイナスのケースがある。 1091 final int edC = rowObj.getLastCellNum(); 1092 for( int colNo=stC; colNo<=edC; colNo++ ) { 1093 final Cell colObj = rowObj.getCell( colNo ); 1094 // if( colObj != null && colObj.getCellType() != Cell.CELL_TYPE_BLANK ) { // 6.5.0.0 (2016/09/30) poi-3.12 1095 if( colObj != null && colObj.getCellTypeEnum() != CellType.BLANK ) { // 6.5.0.0 (2016/09/30) poi-3.15 1096 final String cmnt= "Sht" + shtNo + ":" + POIUtil.getCelKigo( rowNo,colNo ); 1097 final String val = crConv( conv, POIUtil.getValue( colObj ),cmnt ); // 改行対応 1098 if( val != null ) { 1099 POIUtil.setValue( colObj,val ); // 6.3.9.0 (2015/11/06) 1100 // colObj.setCellValue( val ); 1101 } 1102 } 1103 } 1104 } 1105 } 1106 1107 // オブジェクト文字列の変換 1108 if( sht instanceof POIXMLDocumentPart ) { 1109 for( final POIXMLDocumentPart pxdp : ((POIXMLDocumentPart)sht).getRelations() ) { 1110 if( pxdp instanceof XSSFDrawing ) { 1111 for( final XSSFShape shape : ((XSSFDrawing)pxdp).getShapes() ) { 1112 final org.apache.poi.xssf.usermodel.XSSFAnchor anc = shape.getAnchor(); 1113 final String ancSt = "XY(" + anc.getDx1() + "-" + anc.getDy1() + ")" ; 1114 int cnt = 0; 1115 if( shape instanceof XSSFSimpleShape ) { 1116 for( final XSSFTextParagraph para : ((XSSFSimpleShape)shape).getTextParagraphs() ) { 1117 for( final XSSFTextRun text : para.getTextRuns() ) { 1118 final String cmnt= "Sht" + shtNo + ":" + ancSt + ":Tb(" + cnt++ + ")" ; 1119 final String val = crConv( conv,text.getText() , cmnt ); 1120 if( val != null ) { 1121 text.setText( val ); 1122 } 1123 } 1124 } 1125 } 1126 } 1127 } 1128 } 1129 } 1130 // 6.2.5.0 (2015/06/05) xsl形式のオブジェクト取得…はできなかった。 1131 // else if( sht instanceof HSSFSheet ) { 1132 // HSSFPatriarch patri = ((HSSFSheet)sht).getDrawingPatriarch(); 1133 // for( final HSSFShape shape : patri.getChildren() ) { 1134 // if( shape instanceof HSSFTextbox ) { 1135 // HSSFRichTextString rts = ((HSSFSimpleShape)shape).getString(); 1136 // if( rts != null ) { 1137 // final String val = crConv( conv,rts.getString() ); 1138 // if( val != null ) { 1139 // HSSFRichTextString rts2 = new HSSFRichTextString( val ); 1140 // ((HSSFSimpleShape)shape).setString( rts2 ); 1141 // } 1142 // } 1143 // } 1144 // } 1145 // } 1146 } 1147 // } 1148 } 1149 1150 /** 1151 * 現在のシートを選択済み(true)か、非選択済み(false)に設定します。 1152 * 1153 * 通常は、シートは、先頭シート以外は、非選択状態になっています。 1154 * シートを選択済みにすることで、印刷範囲を指定する事ができます。 1155 * 1156 * @og.rev 6.3.9.0 (2015/11/06) 新規追加 1157 * 1158 * @param isSelect true:シート選択/false:非選択 1159 */ 1160 public void sheetSelected( final boolean isSelect ) { 1161 sheet.setSelected( isSelect ); 1162 } 1163 1164 /** 1165 * Workbook の雛形シートのTextConverter した、新しいSheetを作成します。 1166 * 1167 * 正確には、 1168 * 1.雛形シートを、コピーして、新しいSheet(shtName)を、作成します。 1169 * 2.雛形シートが指定されていない場合は、一番最後のシートをコピーします。 1170 * 2.そのシートに対して、TextConverter を行い、文字列変換します。 1171 * 1172 * @og.rev 6.3.9.0 (2015/11/06) 新規追加 1173 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX) 1174 * 1175 * @param conv TextConverterインターフェース 1176 * @param shtName シート名 1177 * @see #textConverter( Map ) 1178 */ 1179 @SuppressWarnings(value={"deprecation"}) // poi-3.15 1180 public void sheetCopy( final TextConverter<String,String> conv , final String shtName ) { 1181 int shtNo = wkbook.getNumberOfSheets() - 1; 1182 if( refSheetIdx >= 0 && refSheetIdx < shtNo ) { // 雛形シートをコピーする。 1183 sheet = wkbook.cloneSheet( refSheetIdx ); 1184 } 1185 else { 1186 sheet = wkbook.cloneSheet( shtNo ); // 最後のシートをコピーします。 1187 } 1188 shtNo++ ; // シート番号を増やしておく。 1189 1190 // シート名の変換 1191 setSheetName( shtNo,shtName ); // 同一シート対策済みのメソッドを呼び出す。 1192 1193 // セル値の変換 1194 final int stR = Math.max( sheet.getFirstRowNum(),0 ); // stR が、マイナスのケースがある。 1195 final int edR = sheet.getLastRowNum(); 1196 1197 for( int rowNo=stR; rowNo<=edR; rowNo++ ) { 1198 final Row rowObj = sheet.getRow( rowNo ); 1199 if( rowObj != null ) { 1200 final int stC = Math.max( rowObj.getFirstCellNum(),0 ); // stC が、マイナスのケースがある。 1201 final int edC = rowObj.getLastCellNum(); 1202 for( int colNo=stC; colNo<=edC; colNo++ ) { 1203 final Cell colObj = rowObj.getCell( colNo ); 1204 // if( colObj != null && colObj.getCellType() != Cell.CELL_TYPE_BLANK ) { // 6.5.0.0 (2016/09/30) poi-3.12 1205 if( colObj != null && colObj.getCellTypeEnum() != CellType.BLANK ) { // 6.5.0.0 (2016/09/30) poi-3.15 1206 final String val = crConv( conv, POIUtil.getValue( colObj ),null ); // 改行対応 1207 if( val != null ) { 1208 POIUtil.setValue( colObj,val ); 1209 // colObj.setCellValue( val ); 1210 } 1211 } 1212 } 1213 } 1214 } 1215 1216 // オブジェクト文字列の変換 1217 if( sheet instanceof POIXMLDocumentPart ) { 1218 for( final POIXMLDocumentPart pxdp : ((POIXMLDocumentPart)sheet).getRelations() ) { 1219 if( pxdp instanceof XSSFDrawing ) { 1220 for( final XSSFShape shape : ((XSSFDrawing)pxdp).getShapes() ) { 1221 // final org.apache.poi.xssf.usermodel.XSSFAnchor anc = shape.getAnchor(); 1222 if( shape instanceof XSSFSimpleShape ) { 1223 for( final XSSFTextParagraph para : ((XSSFSimpleShape)shape).getTextParagraphs() ) { 1224 for( final XSSFTextRun text : para.getTextRuns() ) { 1225 final String val = crConv( conv,text.getText() , null ); 1226 if( val != null ) { 1227 text.setText( val ); 1228 } 1229 } 1230 } 1231 } 1232 } 1233 } 1234 } 1235 } 1236 } 1237 1238 /** 1239 * Workbook の全Sheetを対象に、テキスト変換処理を行います(XSLX限定)。 1240 * 1241 * この処理は、#saveFile( File ) の直前に行うのがよいでしょう。 1242 * #activeWorkbook( boolean ) との順番は構いません。 1243 * 1244 * ・シート名の一覧をピックアップします。 1245 * ・セル値を、セル単位内の改行単位にピックアップし、結果を合成ます。 1246 * ・オブジェクト文字列を、改行単位にピックアップし、結果を合成します。 1247 * 1248 * ここでは、シート名、セルテキスト、SimpleShapeオブジェクトのテキストを 1249 * input に、TextConverterインターフェース の change メソッドを呼び出します。 1250 * 戻り値が、null でないなら、元のデータと置き換えます。 1251 * 戻り値が、null の場合は、そのまま読み飛ばします。(なにもしません) 1252 * EXCELへの書き戻しが発生しますので、万一、ファイル破損で、開けなくなる場合を 1253 * 想定して、バックアップファイルは、各自で準備してください。 1254 * 1255 * @og.rev 6.2.4.2 (2015/05/29) テキスト変換処理 1256 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 1257 * 1258 * @param conv TextConverterインターフェース 1259 * @param val 改行処理を行う元の値 1260 * @param cmnt コメント 1261 * @return 改行処理の結果の値(対象が無ければ、null) 1262 * @see #textConverter( Map ) 1263 */ 1264 private String crConv( final TextConverter<String,String> conv , final String val , final String cmnt ) { 1265 String rtn = null; 1266 if( val != null ) { 1267 if( val.contains( "\n" ) ) { // 改行がある場合(EXCEL のセル内改行コードは、LF(0A)=\n のみ。 1268 final String[] val2 = val.split( "\\n" ); // 改行で分割する。 1269 boolean flag = false; 1270 for( int i=0; i<val2.length; i++ ) { 1271 final String val3 = conv.change( val2[i],cmnt ); // 6.3.1.0 (2015/06/28) 1272 if( val3 != null ) { val2[i] = val3; flag = true; } 1273 } 1274 if( flag ) { 1275 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 1276 buf.append( val2[0] ); 1277 for( int i=1; i<val2.length; i++ ) { 1278 buf.append( '\n' ).append( val2[i] ); // LF(\n)で、セパレートしているので、LF のみ追加する。 1279 } 1280 rtn = buf.toString(); 1281 } 1282 } 1283 else { // 改行がない場合 1284 rtn = conv.change( val,cmnt ); // 6.3.1.0 (2015/06/28) 1285 } 1286 } 1287 return rtn; 1288 } 1289 1290 /** 1291 * シート一覧を、内部の Workbook から取得します。 1292 * 1293 * 取得元が、Workbook なので、xls , xlsx どちらの形式でも取り出せます。 1294 * 1295 * EXCEL上のシート名を、配列で返します。 1296 * 1297 * @og.rev 6.2.6.0 (2015/06/19) 新規作成 1298 * 1299 * @return シート名の配列 1300 * @see POIUtil#getSheetNames( Workbook ) 1301 */ 1302 public String[] getSheetNames() { 1303 return POIUtil.getSheetNames( wkbook ); 1304 } 1305 1306 /** 1307 * 名前定義一覧を内部の Workbook から取得します。 1308 * 1309 * EXCEL上に定義された名前を、配列で返します。 1310 * ここでは、名前とFormulaをタブで連結した文字列を配列で返します。 1311 * Name オブジェクトを削除すると、EXCELが開かなくなったりするので、 1312 * 取りあえず一覧を作成して、手動で削除してください。 1313 * なお、名前定義には、非表示というのがありますので、ご注意ください。 1314 * 1315 * @og.rev 6.2.6.0 (2015/06/19) 新規作成 1316 * 1317 * @return 名前定義(名前+TAB+Formula)の配列 1318 * @see POIUtil#getNames( Workbook ) 1319 * @og.rtnNotNull 1320 */ 1321 public String[] getNames() { 1322 return POIUtil.getNames( wkbook ); 1323 } 1324 1325 /** 1326 * 書式のスタイル一覧を内部の Workbook から取得します。 1327 * 1328 * EXCEL上に定義された書式のスタイルを、配列で返します。 1329 * 書式のスタイルの名称は、CellStyle にメソッドが定義されていません。 1330 * 実クラスである HSSFCellStyle にキャストして使用する 1331 * 必要があります。(XSSFCellStyle にも名称を取得するメソッドがありません。) 1332 * 1333 * ※ EXCEL2010 ホームタブ→セルのスタイル は、一つづつしか削除できません。 1334 * マクロは、開発タブ→Visual Basic で、挿入→標準モジュール を開き 1335 * テキストを張り付けてください。 1336 * 実行は、開発タブ→マクロ で、マクロ名を選択して、実行します。 1337 * 最後は、削除してください。 1338 * 1339 * @og.rev 6.2.6.0 (2015/06/19) 新規作成 1340 * 1341 * @return 書式のスタイル一覧 1342 * @see POIUtil#getStyleNames( Workbook ) 1343 * @og.rtnNotNull 1344 */ 1345 public String[] getStyleNames() { 1346 return POIUtil.getStyleNames( wkbook ); 1347 } 1348 1349 /** 1350 * 文字列を Double オブジェクトに変換します。 1351 * 1352 * これは、引数の カンマ(,) を削除した文字列から、Double オブジェクトを生成します。 1353 * 処理中に、文字列が解析可能な double を含まない場合(NumberFormatException) 1354 * また、引数が、null,ゼロ文字列,'_', エラー の時には、null を返します。 1355 * 1356 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1357 * @og.rev 6.3.9.0 (2015/11/06) もう少し判りやすくする。(処理速度は落ちてます。) 1358 * 1359 * @param value Doubleに変換する元の文字列 1360 * 1361 * @return 変換後のDoubleオブジェクト(エラー発生時や変換不可の場合は、null) 1362 */ 1363 private Double parseDouble( final String value ) { 1364 Double rtn = null ; 1365 1366 try { 1367 if( value == null || value.isEmpty() || value.equals( "_" ) ) { 1368 rtn = null; 1369 } 1370 else if( value.indexOf( ',' ) < 0 ) { 1371 rtn = Double.valueOf( value ); // 6.0.2.4 (2014/10/17) メソッドが非効率だった。 1372 } 1373 else { 1374 // 6.3.9.0 (2015/11/06) もう少し判りやすくする。(処理速度は落ちてます。) 1375 rtn = Double.valueOf( value.replaceAll( ",","" ) ); 1376 } 1377 } 1378 catch( final NumberFormatException ex ) { // 文字列が解析可能な数値を含まない場合 1379 final String errMsg = "Double変換できませんでした。" + CR 1380 + ex.getMessage() + CR 1381 + " value=" + value; 1382 System.err.println( errMsg ); 1383 rtn = null; 1384 } 1385 1386 return rtn ; 1387 } 1388 1389 /** 1390 * アプリケーションのサンプルです。 1391 * 1392 * Usage: java org.opengion.fukurou.model.ExcelModel 入力ファイル名 [出力ファイル名] ・・・ 1393 * 通常は標準出力に行単位に、セルをタブ区切り出力します。 1394 * 出力ファイル名 を指定すると、EXCEL ファイルとしてセーブし直します。 1395 * その場合は、以下のパラメータも使用できます。 1396 * -CS CellStyleを 設定します。 1397 * -AS useAutoCellSizeを 設定します。 1398 * -FN=*** FontNameを 設定します。 1399 * -FP=** FontPointを 設定します。 1400 * -IMG 画像ファイルを挿入します。(-IMG 画像ファイル名 シート番号 行 列)をスペース区切りで続けます。 1401 * 1402 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1403 * 1404 * @param args コマンド引数配列 1405 */ 1406 public static void main( final String[] args ) { 1407 if( args.length == 0 ) { 1408 final String usage = "Usage: java org.opengion.fukurou.model.ExcelModel 入力ファイル名 [出力ファイル名] ・・・\n" + 1409 "\t-CS CellStyleを 設定します。 \n" + 1410 "\t-TC TextConverterを実行します。 \n" + 1411 "\t-AS useAutoCellSizeを 設定します。 \n" + 1412 "\t-FN=*** FontNameを 設定します。 \n" + 1413 "\t-FP=** FontPointを 設定します。 \n" + 1414 "\t-IMG 画像ファイルを挿入します。 \n" + 1415 "\t (-IMG ファイル名 シート番号 行 列) \n" ; 1416 System.err.println( usage ); 1417 return ; 1418 } 1419 1420 final ExcelModel excel = new ExcelModel( new File( args[0] ) , true ); 1421 1422 excel.activeWorkbook( true ); // 余計な行を削除します。 1423 1424 if( args.length > 1 ) { 1425 final File outFile = new File( args[1] ); // 6.2.0.0 (2015/02/27) 1426 boolean isCS = false; 1427 boolean isAS = false; 1428 boolean isTC = false; // 6.2.4.2 (2015/05/29) テキスト変換処理 1429 String fn = null; 1430 short fp = -1; 1431 1432 for( int i=2; i<args.length; i++ ) { 1433 final String prm = args[i]; 1434 1435 if( "-CS".equalsIgnoreCase( prm ) ) { isCS = true; } // 6.4.1.1 (2016/01/16) PMD refactoring. Position literals first in String comparisons for EqualsIgnoreCase. 1436 if( "-AS".equalsIgnoreCase( prm ) ) { isAS = true; } // 6.4.1.1 (2016/01/16) PMD refactoring. Position literals first in String comparisons for EqualsIgnoreCase. 1437 if( "-TC".equalsIgnoreCase( prm ) ) { isTC = true; } // 6.4.1.1 (2016/01/16) PMD refactoring. Position literals first in String comparisons for EqualsIgnoreCase. 1438 if( prm.startsWith( "-FN" ) ) { fn = prm.substring( 3 ); } 1439 if( prm.startsWith( "-FP" ) ) { fp = Short.parseShort( prm.substring( 3 ) ); } 1440 if( "-IMG".equalsIgnoreCase( prm ) ) { // 6.4.1.1 (2016/01/16) PMD refactoring. Position literals first in String comparisons for EqualsIgnoreCase. 1441 final String img = args[++i]; 1442 final int shtNo = Integer.parseInt( args[++i] ); 1443 final int rowNo = Integer.parseInt( args[++i] ); 1444 final int colNo = Integer.parseInt( args[++i] ); 1445 1446 excel.addImageFile( img,shtNo,rowNo,colNo ); 1447 } 1448 } 1449 1450 if( isCS ) { excel.setCellStyle(); } 1451 excel.useAutoCellSize( isAS ); 1452 excel.setFont( fn,fp ); 1453 1454 // 6.2.4.2 (2015/05/29) テキスト変換処理 1455 if( isTC ) { 1456 // 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1457 // 処理が複数行に別れるのは判りにくいので良くない。 1458 excel.textConverter( 1459 ( val,cmnt ) -> { 1460 System.out.println( val ); // すべてのテキストを読み取る。 1461 return null; // 変換せず。 1462 } 1463 ); 1464 } 1465 1466 excel.saveFile( outFile ); 1467 } 1468 else { 1469 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 1470 1471 final int shLen = excel.getNumberOfSheets(); 1472 for( int shtNo=0; shtNo<shLen; shtNo++ ) { 1473 final String shtName = excel.getSheetName( shtNo ); 1474 1475 final int stRow = excel.getFirstRowNum(); 1476 final int edRow = excel.getLastRowNum(); 1477 for( int rowNo=stRow; rowNo<=edRow; rowNo++ ) { 1478 buf.setLength(0); // Clearの事 1479 buf.append( shtName ).append( '\t' ).append( rowNo ); 1480 final String[] vals = excel.getValues( rowNo ); 1481 if( vals != null ) { 1482 for( int colNo=0; colNo<vals.length; colNo++ ) { 1483 final String val = vals[colNo] == null ? "" : vals[colNo]; 1484 buf.append( '\t' ).append( val ); 1485 } 1486 } 1487 System.out.println( buf ); 1488 } 1489 System.out.println(); 1490 } 1491 } 1492 } 1493}