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