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