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