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