    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.InputStream;
    019import java.io.FileInputStream;
    020import java.io.BufferedReader;                                                                                                  // 6.2.2.0 (2015/03/27)
    021import java.io.BufferedInputStream;
    022import java.io.FileNotFoundException;
    023import java.io.File;
    024import java.io.IOException;
    025import java.nio.file.Files;                                                                                                             // 6.2.2.0 (2015/03/27)
    026import java.nio.charset.Charset;                                                                                                // 6.2.2.0 (2015/03/27)
    027
    028import java.util.Set;                                                                                                                   // 6.0.2.3 (2014/10/10)
    029import java.util.TreeSet;                                                                                                               // 6.0.2.3 (2014/10/10)
    030import java.util.List;                                                                                                                  // 6.4.6.0 (2016/05/27) poi-3.15
    031import java.util.ArrayList;                                                                                                             // 8.0.1.0 (2021/10/29)
    032
    033// import org.apache.xmlbeans.XmlException;                                                                             // 8.0.0.0 (2021/07/31) Delete
    034// import org.apache.poi.POITextExtractor;
    035import org.apache.poi.extractor.POITextExtractor;                                                               // 7.0.0.0 (2018/10/01) poi-3.17.jar → poi-4.0.0.jar
    036// import org.apache.poi.extractor.ExtractorFactory;
    037// import org.apache.poi.ooxml.extractor.ExtractorFactory;                                              // 7.0.0.0 (2018/10/01) poi-ooxml-3.17.jar → poi-ooxml-4.0.0.jar
    038import org.apache.poi.ooxml.extractor.POIXMLExtractorFactory;                                   // 8.0.0.0 (2021/07/31) poi-ooxml-4.0.0.jar → poi-ooxml-5.0.0.jar
    039import org.apache.poi.hwpf.HWPFDocument;
    040import org.apache.poi.hwpf.usermodel.Range;
    041import org.apache.poi.hwpf.usermodel.Paragraph;
    042import org.apache.poi.xwpf.usermodel.XWPFDocument;                                                              // 6.2.0.0 (2015/02/27)
    043import org.apache.poi.xwpf.usermodel.XWPFParagraph;                                                             // 6.2.0.0 (2015/02/27)
    044import org.apache.poi.hssf.usermodel.HSSFCellStyle;
    045import org.apache.poi.hslf.usermodel.HSLFTextParagraph;                                                 // 6.4.6.0 (2016/05/27) poi-3.15
    046import org.apache.poi.hslf.usermodel.HSLFSlide;                                                                 // 6.4.6.0 (2016/05/27) poi-3.15
    047import org.apache.poi.hslf.usermodel.HSLFSlideShow;                                                             // 6.4.6.0 (2016/05/27) poi-3.15
    048
    049import org.apache.poi.xslf.usermodel.XMLSlideShow;                                                              // 6.2.0.0 (2015/02/27)
    050// import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor;                                // 7.0.0.0 (2018/10/01) POI4.0.0 deprecation
    051import org.apache.poi.sl.extractor.SlideShowExtractor;                                                  // 7.0.0.0 (2018/10/01) POI4.0.0 XSLFPowerPointExtractor → SlideShowExtractor
    052import org.apache.poi.xslf.usermodel.XSLFShape;                                                                 // 7.0.0.0 (2018/10/01) POI4.0.0 XSLFPowerPointExtractor → SlideShowExtractor
    053import org.apache.poi.xslf.usermodel.XSLFTextParagraph;                                                 // 7.0.0.0 (2018/10/01) POI4.0.0 XSLFPowerPointExtractor → SlideShowExtractor
    054
    055// import org.apache.poi.openxml4j.exceptions.InvalidFormatException;                   // 8.0.0.0 (2021/07/31) Delete
    056// import org.apache.poi.openxml4j.exceptions.OpenXML4JException ;                              // 6.1.0.0 (2014/12/26) findBugs  // 8.0.0.0 (2021/07/31) Delete
    057import org.apache.poi.ss.usermodel.WorkbookFactory;
    058import org.apache.poi.ss.usermodel.Workbook;
    059import org.apache.poi.ss.usermodel.Sheet;
    060import org.apache.poi.ss.usermodel.Row;
    061import org.apache.poi.ss.usermodel.Cell;
    062import org.apache.poi.ss.usermodel.CellStyle;
    063import org.apache.poi.ss.usermodel.CreationHelper;
    064import org.apache.poi.ss.usermodel.RichTextString;
    065import org.apache.poi.ss.usermodel.DateUtil;
    066import org.apache.poi.ss.usermodel.FormulaEvaluator;
    067import org.apache.poi.ss.usermodel.Name;                                                                                // 6.0.2.3 (2014/10/10)
    068import org.apache.poi.ss.usermodel.CellType;                                                                    // 6.5.0.0 (2016/09/30) poi-3.15
    069import org.apache.poi.ss.util.SheetUtil;
    070
    071import org.opengion.fukurou.system.OgRuntimeException ;                                                 // 6.4.2.0 (2016/01/29)
    072import org.opengion.fukurou.util.FileInfo;                                                                              // 6.2.3.0 (2015/05/01)
    073import org.opengion.fukurou.system.ThrowUtil;                                                                   // 6.4.2.0 (2016/01/29)
    074import org.opengion.fukurou.system.Closer;                                                                              // 6.2.0.0 (2015/02/27)
    075import static org.opengion.fukurou.system.HybsConst.CR;                                                 // 6.1.0.0 (2014/12/26) refactoring
    076import static org.opengion.fukurou.system.HybsConst. BUFFER_MIDDLE ;                    // 6.4.2.1 (2016/02/05) refactoring
    077
    078/**
    079 * POI による､Excel/Word/PoworPoint等に対する､ﾕｰﾃｨﾘﾃｨｸﾗｽです｡
    080 *
    081 * 基本的には､ﾈｲﾃｨﾌﾞﾌｧｲﾙを読み取り､ﾃｷｽﾄを取得する機能が主です｡
    082 * Excel､Word､PowerPoint､Visio､Publisher からのﾃｷｽﾄ取得が可能です｡
    083 *
    084 * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    085 * @og.rev 6.2.0.0 (2015/02/27) ﾊﾟｯｹｰｼﾞ変更(util → model)
    086 * @og.group その他
    087 *
    088 * @version  6.0
    089 * @author   Kazuhiko Hasegawa
    090 * @since    JDK7.0,
    091 */
    092public final class POIUtil {
    093        /** このﾌﾟﾛｸﾞﾗﾑのVERSION文字列を設定します｡ {@value} */
    094        private static final String VERSION = "8.0.1.0 (2021/10/29)" ;
    095
    096        // 6.2.3.0 (2015/05/01)
    097        /** 対象ｻﾌｨｯｸｽ {@value} */
    098        public static final String POI_SUFIX = "ppt,pptx,doc,docx,xls,xlsx,xlsm" ;
    099
    100        /**
    101         * すべてが staticﾒｿｯﾄﾞなので､ｺﾝｽﾄﾗｸﾀを呼び出さなくしておきます｡
    102         *
    103         */
    104        private POIUtil() {}
    105
    106        /**
    107         * 引数ﾌｧｲﾙが､POI関連の拡張子ﾌｧｲﾙかどうかを判定します｡
    108         *
    109         * Excel､Word､PowerPoint､Visio､Publisher からのﾃｷｽﾄ取得が可能です｡
    110         * 拡張子が､ppt,pptx,doc,docx,xls,xlsx,xlsm のﾌｧｲﾙの場合､true を返します｡
    111         *
    112         * @og.rev 6.2.3.0 (2015/05/01) POI関連の拡張子ﾌｧｲﾙかどうかを判定
    113         *
    114         * @param       file 判定するﾌｧｲﾙ
    115         * @return      POI関連の拡張子の場合､true
    116         */
    117        public static boolean isPOI( final File file ) {
    118                return POI_SUFIX.contains( FileInfo.getSUFIX( file ) );
    119        }
    120
    121        /**
    122         * 引数ﾌｧｲﾙを､POITextExtractor を使用してﾃｷｽﾄ化します｡
    123         *
    124         * Excel､Word､PowerPoint､Visio､Publisher からのﾃｷｽﾄ取得が可能です｡
    125         * 拡張子から､ﾌｧｲﾙの種類を自動判別します｡
    126         *
    127         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    128         * @og.rev 6.2.0.0 (2015/02/27) getText → extractor に変更
    129         * @og.rev 8.0.0.0 (2021/07/31) ExtractorFactory → POIXMLExtractorFactory に変更
    130         *
    131         * @param       file 入力ﾌｧｲﾙ名
    132         * @return      変換後のﾃｷｽﾄ
    133         * @og.rtnNotNull
    134         */
    135        public static String extractor( final File file ) {
    136        //      InputStream fis = null;
    137                POITextExtractor extractor = null;
    138                try {
    139        //              fis = new BufferedInputStream( new FileInputStream( file ) );
    140        //              extractor = ExtractorFactory.createExtractor( fis );
    141        //              extractor = ExtractorFactory.createExtractor( file );
    142                        extractor = new POIXMLExtractorFactory().create( file , null );         // 8.0.0.0 (2021/07/31) poi-ooxml-4.0.0.jar → poi-ooxml-5.0.0.jar
    143                        return extractor.getText();
    144                }
    145                catch( final FileNotFoundException ex ) {
    146                        final String errMsg = "ﾌｧｲﾙが存在しません[" + file + "]" + CR + ex.getMessage() ;
    147                        throw new OgRuntimeException( errMsg,ex );
    148                }
    149                catch( final IOException ex ) {
    150                        final String errMsg = "ﾌｧｲﾙ処理ｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    151                        throw new OgRuntimeException( errMsg,ex );
    152                }
    153                // 8.0.0.0 (2021/07/31) poi-ooxml-4.0.0.jar → poi-ooxml-5.0.0.jar
    154//              catch( final InvalidFormatException ex ) {
    155//                      final String errMsg = "ﾌｧｲﾙﾌｫｰﾏｯﾄｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    156//                      throw new OgRuntimeException( errMsg,ex );
    157//              }
    158//              catch( final OpenXML4JException ex ) {
    159//                      final String errMsg = "ODF-XML処理ｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    160//                      throw new OgRuntimeException( errMsg,ex );
    161//              }
    162//              catch( final XmlException ex ) {
    163//                      final String errMsg = "XML処理ｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    164//                      throw new OgRuntimeException( errMsg,ex );
    165//              }
    166                finally {
    167                        Closer.ioClose( extractor );
    168        //              Closer.ioClose( fis );
    169                }
    170        }
    171
    172        /**
    173         * 引数ﾌｧｲﾙ(Text)を､ﾃｷｽﾄ化します｡
    174         *
    175         * ここでは､ﾌｧｲﾙとｴﾝｺｰﾄﾞを指定して､ﾌｧｲﾙのﾃｷｽﾄ全てを読み取ります｡
    176         *
    177         * @og.rev 6.2.2.0 (2015/03/27) 引数ﾌｧｲﾙ(Text)を､ﾃｷｽﾄ化｡
    178         * @og.rev 6.2.3.0 (2015/05/01) textReader → extractor に変更
    179         *
    180         * @param       file 入力ﾌｧｲﾙ
    181         * @param       encode ｴﾝｺｰﾄﾞ名
    182         * @return      ﾌｧｲﾙのﾃｷｽﾄ
    183         */
    184        public static String extractor( final File file , final String encode ) {
    185                try {
    186                        // 指定のﾌｧｲﾙをﾊﾞｲﾄ列として読み込む
    187                        final byte[] bytes = Files.readAllBytes( file.toPath() );
    188                        // 読み込んだﾊﾞｲﾄ列を ｴﾝｺｰﾄﾞして文字列にする
    189                        return new String( bytes, encode );
    190                }
    191        //      catch( final UnsupportedEncodingException ex ) {
    192        //              final String errMsg = "ｴﾝｺｰﾄﾞが不正です[" + file + "] , ENCODE=[" + encode + "]"  ;
    193        //              throw new OgRuntimeException( errMsg,ex );
    194        //      }
    195                catch( final IOException ex ) {
    196                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "] , ENCODE=[" + encode + "]"  ;
    197                        throw new OgRuntimeException( errMsg,ex );
    198                }
    199        }
    200
    201        /**
    202         * 引数ﾌｧｲﾙ(Text)を､ﾃｷｽﾄ化します｡
    203         *
    204         * ここでは､ﾌｧｲﾙとｴﾝｺｰﾄﾞを指定して､ﾌｧｲﾙのﾃｷｽﾄ全てを読み取ります｡
    205         *
    206         * @og.rev 6.2.2.0 (2015/03/27) 引数ﾌｧｲﾙ(Text)を､ﾃｷｽﾄ化｡
    207         *
    208         * @param       file 入力ﾌｧｲﾙ
    209         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    210         * @param       encode ｴﾝｺｰﾄﾞ名
    211         */
    212        public static void textReader( final File file , final TextConverter<String,String> conv , final String encode ) {
    213                BufferedReader reader = null ;
    214
    215                int rowNo = 0;          // 6.2.0.0 (2015/02/27) 行番号
    216                try {
    217                        reader = Files.newBufferedReader( file.toPath() , Charset.forName( encode ) );
    218
    219                        String line ;
    220                        while((line = reader.readLine()) != null) {
    221                                conv.change( line,String.valueOf( rowNo++ ) );
    222                        }
    223                }
    224                catch( final IOException ex ) {
    225                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "] , ENCODE=[" + encode + "] , ROW=[" + rowNo + "]"  ;
    226                        throw new OgRuntimeException( errMsg,ex );
    227                }
    228                finally {
    229                        Closer.ioClose( reader );
    230                }
    231        }
    232
    233        /**
    234         * 引数ﾌｧｲﾙ(Word,PoworPoint,Excel)を､TableModelHelper を使用してﾃｷｽﾄ化します｡
    235         *
    236         * ここでは､ﾌｧｲﾙ名の拡張子で､処理するﾒｿｯﾄﾞを選別します｡
    237         * 拡張子が､対象かどうかは､#isPOI( File ) ﾒｿｯﾄﾞで判定できます｡
    238         *
    239         * TableModelHelper によるｲﾍﾞﾝﾄ処理できますが､TEXTというｶﾗﾑ名を持つ
    240         * 表形式ｵﾌﾞｼﾞｪｸﾄの形で処理されます｡
    241         * また､内部的に､先頭に､# がある場合や､行ﾃﾞｰﾀが存在しない場合は､
    242         * ｽｷｯﾌﾟされます｡
    243         *
    244         * @og.rev 6.2.3.0 (2015/05/01) 新規作成
    245         * @og.rev 6.2.5.0 (2015/06/05) xls,xlsxは､それぞれ excelReader1,excelReader2 で処理します｡
    246         *
    247         * @param       file 入力ﾌｧｲﾙ
    248         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    249         */
    250        public static void textReader( final File file , final TextConverter<String,String> conv ) {
    251                final String SUFIX = FileInfo.getSUFIX( file );
    252
    253                if( "doc".equalsIgnoreCase( SUFIX ) ) {
    254                        wordReader1( file,conv );
    255                }
    256                else if( "docx".equalsIgnoreCase( SUFIX ) ) {
    257                        wordReader2( file,conv );
    258                }
    259                else if( "ppt".equalsIgnoreCase( SUFIX ) ) {
    260                        pptReader1( file,conv );
    261                }
    262                else if( "pptx".equalsIgnoreCase( SUFIX ) ) {
    263                        pptReader2( file,conv );
    264                }
    265                else if( "xls".equalsIgnoreCase( SUFIX ) ) {
    266                        excelReader1( file,conv );                                                              // 6.2.5.0 (2015/06/05)
    267                }
    268                else if( "xlsx".equalsIgnoreCase( SUFIX ) || "xlsm".equalsIgnoreCase( SUFIX ) ) {
    269                        excelReader2( file,conv );                                                              // 6.2.5.0 (2015/06/05)
    270                }
    271                else {
    272                        final String errMsg = "拡張子は､" +  POI_SUFIX + " にしてください｡[" + file + "]" ;
    273                        throw new OgRuntimeException( errMsg );
    274                }
    275        }
    276
    277        /**
    278         * 引数ﾌｧｲﾙ(Word)を､HWPFDocument を使用してﾃｷｽﾄ化します｡
    279         *
    280         * 拡張子(.doc)のﾌｧｲﾙを処理します｡
    281         * TableModelHelper によるｲﾍﾞﾝﾄ処理できますが､TEXTというｶﾗﾑ名を持つ
    282         * 表形式ｵﾌﾞｼﾞｪｸﾄの形で処理されます｡
    283         * また､内部的に､先頭に､# がある場合や､行ﾃﾞｰﾀが存在しない場合は､
    284         * ｽｷｯﾌﾟされます｡
    285         *
    286         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    287         * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更
    288         * @og.rev 6.2.4.2 (2015/05/29) 改行以外に､｢｡｣で分割します｡
    289         *
    290         * @param       file 入力ﾌｧｲﾙ名
    291         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    292         */
    293        private static void wordReader1( final File file , final TextConverter<String,String> conv ) {
    294                InputStream fis  = null;
    295
    296                try {
    297                        // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正
    298
    299                        fis = new BufferedInputStream( new FileInputStream( file ) );           // 6.2.0.0 (2015/02/27)
    300                        final HWPFDocument doc = new HWPFDocument( fis );
    301
    302                        int rowNo = 0;          // 6.2.0.0 (2015/02/27) 行番号
    303
    304        //              // WordExtractor を使ったｻﾝﾌﾟﾙ
    305        //              WordExtractor we = new WordExtractor( doc );
    306        //              for( String txt : we.getParagraphText() ) {
    307        //                      String text = WordExtractor.stripFields( txt )
    308        //                                                      .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" )
    309        //                                                      .replaceAll( "\\x0b" , "\n" ).trim();
    310        //                      helper.value( text.trim(),rowNo++,0 );                          // 6.2.0.0 (2015/02/27) ｲﾍﾞﾝﾄ変更
    311        //              }
    312
    313                        // Range,Paragraph を使ったｻﾝﾌﾟﾙ
    314                        final Range rng = doc.getRange();
    315                        for( int pno=0; pno<rng.numParagraphs(); pno++ ) {
    316                                final Paragraph para = rng.getParagraph(pno);
    317                                final String text = Range.stripFields( para.text() )
    318                                                                .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" )
    319                                                                .replaceAll( "\\x0b" , "\n" ).trim();
    320                                conv.change( text, String.valueOf( rowNo++ ) );
    321                        }
    322
    323                        // Range,Paragraph,CharacterRun を使ったｻﾝﾌﾟﾙ(変な個所で文字が分断される)
    324        //              final Range rng = doc.getRange();
    325        //              for( int pno = 0; pno < rng.numParagraphs(); pno++ ) {
    326        //                      final Paragraph para = rng.getParagraph(pno);
    327        //                      for( int cno = 0; cno < para.numCharacterRuns(); cno++ ) {
    328        //                              final CharacterRun crun = para.getCharacterRun(cno);
    329        //                              String text = Range.stripFields( crun.text() )
    330        //                                                              .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" )
    331        //                                                              .replaceAll( "\\x0b" , "\n" ).trim();
    332        //                              helper.value( text,rowNo++,0 );                         // 6.2.0.0 (2015/02/27) ｲﾍﾞﾝﾄ変更
    333        //                      }
    334        //              }
    335                }
    336                catch( final IOException ex ) {
    337                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    338                        throw new OgRuntimeException( errMsg,ex );
    339                }
    340                finally {
    341                        Closer.ioClose( fis );
    342                }
    343        }
    344
    345        /**
    346         * 引数ﾌｧｲﾙ(Word)を､XWPFDocument を使用してﾃｷｽﾄ化します｡
    347         *
    348         * 拡張子(.docx)のﾌｧｲﾙを処理します｡
    349         * TableModelHelper によるｲﾍﾞﾝﾄ処理できますが､TEXTというｶﾗﾑ名を持つ
    350         * 表形式ｵﾌﾞｼﾞｪｸﾄの形で処理されます｡
    351         * また､内部的に､先頭に､# がある場合や､行ﾃﾞｰﾀが存在しない場合は､
    352         * ｽｷｯﾌﾟされます｡
    353         *
    354         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    355         * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更
    356         * @og.rev 6.2.4.2 (2015/05/29) 改行以外に､｢｡｣で分割します｡
    357         *
    358         * @param       file 入力ﾌｧｲﾙ
    359         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    360         */
    361        private static void wordReader2( final File file , final TextConverter<String,String> conv ) {
    362                InputStream fis  = null;
    363
    364                try {
    365                        // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正
    366
    367                        fis = new BufferedInputStream( new FileInputStream( file ) );           // 6.2.0.0 (2015/02/27)
    368                        final XWPFDocument doc = new XWPFDocument( fis );
    369
    370                        int rowNo = 0;          // 6.2.0.0 (2015/02/27) 行番号
    371                        for( final XWPFParagraph para : doc.getParagraphs() ) {
    372        //                      for( final XWPFRun run : para.getRuns() ) {
    373        //                              helper.value( run.toString(),rowNo++,0 );                               // 6.2.0.0 (2015/02/27) ｲﾍﾞﾝﾄ変更
    374        //                      }
    375                                final String text = para.getParagraphText().trim();
    376                                conv.change( text, String.valueOf( rowNo++ ) );
    377                        }
    378                }
    379                catch( final IOException ex ) {
    380                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    381                        throw new OgRuntimeException( errMsg,ex );
    382                }
    383                finally {
    384                        Closer.ioClose( fis );
    385                }
    386        }
    387
    388        /**
    389         * 引数ﾌｧｲﾙ(PoworPoint)を､HSLFSlideShow を使用してﾃｷｽﾄ化します｡
    390         *
    391         * 拡張子(.ppt)のﾌｧｲﾙを処理します｡
    392         * TableModelHelper によるｲﾍﾞﾝﾄ処理できますが､TEXTというｶﾗﾑ名を持つ
    393         * 表形式ｵﾌﾞｼﾞｪｸﾄの形で処理されます｡
    394         * また､内部的に､先頭に､# がある場合や､行ﾃﾞｰﾀが存在しない場合は､
    395         * ｽｷｯﾌﾟされます｡
    396         *
    397         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    398         * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更
    399         * @og.rev 6.4.6.0 (2016/05/27) poi-3.15 準備
    400         *
    401         * @param       file 入力ﾌｧｲﾙ
    402         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    403         */
    404        private static void pptReader1( final File file , final TextConverter<String,String> conv ) {
    405                InputStream fis  = null;
    406
    407                try {
    408                        // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正
    409
    410                        fis = new BufferedInputStream( new FileInputStream( file ) );           // 6.2.0.0 (2015/02/27)
    411
    412        //              6.4.6.0 (2016/05/27) poi-3.15
    413                        final HSLFSlideShow ss = new HSLFSlideShow( fis );
    414                        final List<HSLFSlide> slides = ss.getSlides();                                          // 6.4.6.0 (2016/05/27) poi-3.15
    415                        int rowNo = 0;          // 6.2.0.0 (2015/02/27) 行番号
    416                        for( final HSLFSlide  slide : slides ) {                                                        // 6.4.6.0 (2016/05/27) poi-3.15
    417                                for( final List<HSLFTextParagraph> txtList : slide.getTextParagraphs() ) {      // 6.4.6.0 (2016/05/27) poi-3.15
    418                                        final String text = HSLFTextParagraph.getText( txtList );
    419                                        if( text.length() > 0 ) {
    420                                                conv.change( text, String.valueOf( rowNo++ ) );
    421                                        }
    422                                }
    423                        }
    424
    425        //              6.4.6.0 (2016/05/27) poi-3.12
    426        //              final SlideShow ss = new SlideShow( new HSLFSlideShow( fis ) );
    427        //              final Slide[] slides = ss.getSlides();
    428        //              int rowNo = 0;          // 6.2.0.0 (2015/02/27) 行番号
    429        //              for( int sno=0; sno<slides.length; sno++ ) {
    430        //                      final TextRun[] textRun = slides[sno].getTextRuns();
    431        //                      for( int tno=0; tno<textRun.length; tno++ ) {
    432        //                              final String text = textRun[tno].getText();
    433        //                              // ﾃﾞｰﾀとして設定されているﾚｺｰﾄﾞのみｲﾍﾞﾝﾄを発生させる｡
    434        //                              if( text.length() > 0 ) {
    435        //                                      conv.change( text, String.valueOf( rowNo++ ) );
    436        //                              }
    437        //                      }
    438        //              }
    439                }
    440                catch( final IOException ex ) {
    441                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    442                        throw new OgRuntimeException( errMsg,ex );
    443                }
    444                finally {
    445                        Closer.ioClose( fis );
    446                }
    447        }
    448
    449        /**
    450         * 引数ﾌｧｲﾙ(PoworPoint)を､XMLSlideShow を使用してﾃｷｽﾄ化します｡
    451         *
    452         * 拡張子(.pptx)のﾌｧｲﾙを処理します｡
    453         * TableModelHelper によるｲﾍﾞﾝﾄ処理できますが､TEXTというｶﾗﾑ名を持つ
    454         * 表形式ｵﾌﾞｼﾞｪｸﾄの形で処理されます｡
    455         * また､内部的に､先頭に､# がある場合や､行ﾃﾞｰﾀが存在しない場合は､
    456         * ｽｷｯﾌﾟされます｡
    457         *
    458         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    459         * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更
    460         * @og.rev 6.2.4.2 (2015/05/29) 行単位に､取り込むようにします｡
    461         * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] org.apache.poi.xslf.extractorのXSLFPowerPointExtractorは推奨されません (POI4.0.0)
    462         *
    463         * @param       file 入力ﾌｧｲﾙ
    464         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    465         */
    466        private static void pptReader2( final File file , final TextConverter<String,String> conv ) {
    467                InputStream fis  = null;
    468
    469                try {
    470                        // 6.2.0.0 (2015/02/27) TableModelEvent 変更に伴う修正
    471
    472                        fis = new BufferedInputStream( new FileInputStream( file ) );           // 6.2.0.0 (2015/02/27)
    473                        final XMLSlideShow ss = new XMLSlideShow( fis );
    474//                      final XSLFPowerPointExtractor ext = new XSLFPowerPointExtractor( ss );
    475                        final SlideShowExtractor<XSLFShape,XSLFTextParagraph> ext = new SlideShowExtractor<>( ss );             // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated.
    476                        final String[] vals = ext.getText().split( "\\n" );             // 6.2.4.2 (2015/05/29) 行単位に､取り込むようにします｡
    477                        for( int row=0; row<vals.length; row++ ) {
    478                                conv.change( vals[row], String.valueOf( row ) );
    479                        }
    480
    481        //              final XSLFSlide[] slides = ss.getSlides();
    482        //              final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    483        //              int rowNo = 0;          // 6.2.0.0 (2015/02/27) 行番号
    484        //              for( int sno = 0; sno < slides.length; sno++ ) {
    485        //                      buf.setLength(0);               // Clearの事
    486    //
    487        //      //              final XSLFTextShape[] shp = slides[sno].getPlaceholders();
    488        //                      final XSLFShape[] shp = slides[sno].getShapes();
    489        //                      for( int tno = 0; tno < shp.length; tno++ ) {
    490        //      //                      buf.append( shp[tno].getText() );
    491        //                              buf.append( shp[tno].toString() );
    492        //                      }
    493        //      //              String text = buf.toString().trim();
    494        //      //              event.value( text,rowNo++,0 );                                  // 6.2.0.0 (2015/02/27) ｲﾍﾞﾝﾄ変更
    495        //                      helper.value( buf.toString(),rowNo++,0 );               // 6.2.4.2 (2015/05/29) trim() しません｡
    496        //              }
    497                }
    498                catch( final IOException ex ) {
    499                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    500                        throw new OgRuntimeException( errMsg,ex );
    501                }
    502                finally {
    503                        Closer.ioClose( fis );
    504                }
    505        }
    506
    507        /**
    508         * 引数ﾌｧｲﾙ(Excel)を､ﾃｷｽﾄ化します｡
    509         *
    510         * TableModelHelper を与えることで､EXCELﾃﾞｰﾀをﾃｷｽﾄ化できます｡
    511         * ここでは､HSSF(.xls)形式を処理します｡
    512         * ｼｰﾄ名､ｾﾙ､ﾃｷｽﾄｵﾌﾞｼﾞｪｸﾄをﾃｷｽﾄ化します｡
    513         *
    514         * @og.rev 6.2.5.0 (2015/06/05) 新規作成
    515         *
    516         * @param       file 入力ﾌｧｲﾙ
    517         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    518         * @see         org.opengion.fukurou.model.ExcelModel
    519         */
    520        public static void excelReader1( final File file , final TextConverter<String,String> conv ) {
    521                excelReader2( file , conv );
    522        }
    523
    524        /**
    525         * 引数ﾌｧｲﾙ(Excel)を､ﾃｷｽﾄ化します｡
    526         *
    527         * TableModelHelper を与えることで､EXCELﾃﾞｰﾀをﾃｷｽﾄ化できます｡
    528         * ここでは､ExcelModelを使用して､(.xlsx , .xlsm)形式を処理します｡
    529         * ｼｰﾄ名､ｾﾙ､ﾃｷｽﾄｵﾌﾞｼﾞｪｸﾄをﾃｷｽﾄ化します｡
    530         *
    531         * @og.rev 6.2.5.0 (2015/06/05) 新規作成
    532         * @og.rev 6.3.1.0 (2015/06/28) TextConverterに､引数(cmnt)を追加
    533         * @og.rev 6.3.9.0 (2015/11/06) Java 8 ﾗﾑﾀﾞ式に変更
    534         *
    535         * @param       file 入力ﾌｧｲﾙ
    536         * @param       conv   ｲﾍﾞﾝﾄ処理させるI/F
    537         * @see         org.opengion.fukurou.model.ExcelModel
    538         */
    539        public static void excelReader2( final File file , final TextConverter<String,String> conv ) {
    540                final ExcelModel excel = new ExcelModel( file, true );
    541
    542                // 6.3.9.0 (2015/11/06) Java 8 ﾗﾑﾀﾞ式に変更
    543                // textConverter を使いますが､ﾃｷｽﾄを読み込むだけで､変換しません｡
    544                excel.textConverter(
    545                        ( val,cmnt ) -> {
    546                                conv.change( val,cmnt );        // 変換したくないので､引数の TextConverter を直接渡せない｡
    547                                return null;                            // nullを返せば､変換しません｡
    548                        }
    549                );
    550        }
    551
    552        /**
    553         * Excelの行列記号を､行番号と列番号に分解します｡
    554         *
    555         * Excelの行列記号とは､A1 , B5 , AA23 などの形式を指します｡
    556         * これを､行番号と列番号に分解します｡例えば､A1→0行0列､B5→4行1列､AA23→22行26列 となります｡
    557         * 分解した結果は､内部変数の､rowNo と colNo にｾｯﾄされます｡
    558         * これらは､0 から始まる int型の数字で表します｡
    559         *
    560         *   ①行-列形式
    561         *     行列は､EXCELｵﾌﾞｼﾞｪｸﾄに準拠するため､０から始まる整数です｡
    562         *     0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります｡
    563         *   ②EXCEL表記
    564         *     EXCEL表記に準拠した､A1,A2,B1 の記述も処理できるように対応します｡
    565         *     なお､A1,A2,B1 の記述は､必ず､英字1文字＋数字 にしてください｡(A～Zまで)
    566         *   ③EXCELｼｰﾄ名をｷｰに割り当てるために､"SHEET" という記号に対応します｡
    567         *     rowNo = -1 をｾｯﾄします｡
    568         *
    569         * @og.rev 6.0.3.0 (2014/11/13) 新規作成
    570         * @og.rev 6.2.6.0 (2015/06/19) 行-列形式と､SHEET文字列判定を採用｡
    571         *
    572         * @param       kigo    Excelの行列記号( A1 , B5 , AA23 など )
    573         * @return      行と列の番号を持った配列([0]=行=ROW , [1]=列=COL)
    574         * @og.rtnNotNull
    575         */
    576        public static int[] kigo2rowCol( final String kigo ) {
    577                int rowNo = 0;
    578                int colNo = -1;                                                                         // +1 して､26 かける処理をしているので､辻褄合わせ
    579
    580                // 6.2.6.0 (2015/06/19) 行-列形式と､SHEET文字列判定を採用｡
    581                if( "SHEET".equalsIgnoreCase( kigo ) ) {
    582                        rowNo = -1;
    583                }
    584                else {
    585                        final int adrs = kigo.indexOf( '-' );
    586                        if( adrs > 0 ) {
    587                                rowNo = Integer.parseInt( kigo.substring( 0,adrs ) );
    588                                colNo = Integer.parseInt( kigo.substring( adrs+1 ) );
    589                        }
    590                        else {
    591                                for( int i=0; i<kigo.length(); i++ ) {
    592                                        final char ch = kigo.charAt(i);
    593                                        if( 'A' <= ch && ch <= 'Z' ) { colNo = (colNo+1)*26 + ch-'A'; }
    594                                        else {
    595                                                // ｱﾙﾌｧﾍﾞｯﾄでなくなったら､残りは 行番号(ただし､-1する)
    596                                                rowNo = Integer.parseInt( kigo.substring( i ) ) -1;
    597                                                break;
    598                                        }
    599                                }
    600                        }
    601                }
    602                return new int[] { rowNo,colNo };
    603        }
    604
    605        /**
    606         * ｾﾙｵﾌﾞｼﾞｪｸﾄ(Cell)から値を取り出します｡
    607         *
    608         * ｾﾙｵﾌﾞｼﾞｪｸﾄが存在しない場合は､null を返します｡
    609         * それ以外で､うまく値を取得できなかった場合は､ｾﾞﾛ文字列を返します｡
    610         *
    611         * @og.rev 3.8.5.3 (2006/08/07) 取り出し方法を少し修正
    612         * @og.rev 5.5.1.2 (2012/04/06) ﾌｫｰﾏｯﾄｾﾙを実行して､その結果を再帰的に処理する｡
    613         * @og.rev 6.0.3.0 (2014/11/13) ｾﾙﾌｫｰﾏｯﾄｴﾗｰ時に､RuntimeException を throw しない｡
    614         * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を､ThrowUtil#ogStackTrace(String,Throwable) に置き換え｡
    615         * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX)
    616         * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0)
    617         * @og.rev 7.3.0.0 (2021/01/06) ﾌｫｰﾏｯﾄｴﾗｰ時に､ｴﾗｰﾒｯｾｰｼﾞ取得でもｴﾗｰになる｡
    618         * @og.rev 8.0.0.0 (2021/07/31) FORMULA処理のｴﾗｰ対応(出来るだけ…)
    619         *
    620         * @param       oCell EXCELのｾﾙｵﾌﾞｼﾞｪｸﾄ
    621         *
    622         * @return      ｾﾙの値
    623         */
    624        public static String getValue( final Cell oCell ) {
    625                if( oCell == null ) { return null; }
    626                String strText = "";
    627        //      final int nCellType = oCell.getCellType();                                                                      // 6.5.0.0 (2016/09/30) poi-3.12
    628        //      switch(nCellType) {                                                                                                                     // 6.5.0.0 (2016/09/30) poi-3.12
    629//              switch( oCell.getCellTypeEnum() ) {                                                                                     // 6.5.0.0 (2016/09/30) poi-3.15
    630                switch( oCell.getCellType() ) {                                                                                         // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated.
    631        //              case Cell.CELL_TYPE_NUMERIC:                                                                                    // 6.5.0.0 (2016/09/30) poi-3.12
    632                        case NUMERIC:                                                                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
    633                                        strText = getNumericTypeString( oCell );
    634                                        break;
    635        //              case Cell.CELL_TYPE_STRING:                                                                                             // 6.5.0.0 (2016/09/30) poi-3.12
    636                        case STRING:                                                                                                                    // 6.5.0.0 (2016/09/30) poi-3.15
    637        // POI3.0               strText = oCell.getStringCellValue();
    638                                        final RichTextString richText = oCell.getRichStringCellValue();
    639                                        if( richText != null ) {
    640                                                strText = richText.getString();
    641                                        }
    642                                        break;
    643        //              case Cell.CELL_TYPE_FORMULA:                                                                                    // 6.5.0.0 (2016/09/30) poi-3.12
    644                        case FORMULA:                                                                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
    645        // POI3.0               strText = oCell.getStringCellValue();
    646                                        // 5.5.1.2 (2012/04/06) ﾌｫｰﾏｯﾄｾﾙを実行して､その結果を再帰的に処理する｡
    647                                //      final Workbook wb = oCell.getSheet().getWorkbook();
    648                                //      final CreationHelper crateHelper = wb.getCreationHelper();
    649                                //      final FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator();
    650
    651                                        try {
    652                                                strText = oCell.getCellFormula();
    653                                                // 8.0.0.0 (2021/07/31) FORMULA処理のｴﾗｰ対応(出来るだけ…)
    654                                //              final Cell fCell = evaluator.evaluateInCell(oCell);
    655                                //              strText = getValue( fCell );
    656                                        }
    657                                        catch( final Throwable th ) {
    658                                                // 7.3.0.0 (2021/01/06) ﾌｫｰﾏｯﾄｴﾗｰ時に､ｴﾗｰﾒｯｾｰｼﾞ取得でもｴﾗｰになる｡
    659                                                final String errMsg = "ｾﾙﾌｫｰﾏｯﾄが解析できません｡";
    660                        //                      final String errMsg = "ｾﾙﾌｫｰﾏｯﾄが解析できません｡Formula=[" + oCell.getCellFormula() + "]";
    661                        //                                              + CR + getCellMsg( oCell );
    662        //                                      throw new OgRuntimeException( errMsg,th );
    663                                                System.err.println( ThrowUtil.ogStackTrace( errMsg,th ) );      // 6.4.2.0 (2016/01/29)
    664                                        }
    665                                        break;
    666        //              case Cell.CELL_TYPE_BOOLEAN:                                                                                    // 6.5.0.0 (2016/09/30) poi-3.12
    667                        case BOOLEAN:                                                                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
    668                                        strText = String.valueOf(oCell.getBooleanCellValue());
    669                                        break;
    670        //              case Cell.CELL_TYPE_BLANK :                                                                                             // 6.5.0.0 (2016/09/30) poi-3.12
    671                        case BLANK :                                                                                                                    // 6.5.0.0 (2016/09/30) poi-3.15
    672                                        break;
    673        //              case Cell.CELL_TYPE_ERROR:                                                                                              // 6.5.0.0 (2016/09/30) poi-3.12
    674                        case ERROR:                                                                                                                             // 6.5.0.0 (2016/09/30) poi-3.15
    675                                        break;
    676                        default :
    677                                break;
    678                }
    679                return strText ;
    680        }
    681
    682        /**
    683         * ｾﾙｵﾌﾞｼﾞｪｸﾄ(Cell)に､値をｾｯﾄします｡
    684         *
    685         * ｾﾙｵﾌﾞｼﾞｪｸﾄが存在しない場合は､何もしません｡
    686         * 引数は､文字列で渡しますが､ｾﾙの形式に合わせて､変換します｡
    687         * 変換がうまくいかなかった場合は､ｴﾗｰになりますので､ご注意ください｡
    688         *
    689         * @og.rev 6.3.9.0 (2015/11/06) 新規追加
    690         * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX)
    691         * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0)
    692         * @og.rev 7.3.0.0 (2021/01/06) setCellType( CellType.BLANK )(Deprecated) → setBlank() (poi-4.1.2)
    693         *
    694         * @param       oCell EXCELのｾﾙｵﾌﾞｼﾞｪｸﾄ
    695         * @param       val   ｾｯﾄする値
    696         */
    697        public static void setValue( final Cell oCell , final String val ) {
    698                if( oCell == null ) { return ; }
    699        //      if( val == null || val.isEmpty() ) { oCell.setCellType( Cell.CELL_TYPE_BLANK ); }               // 6.5.0.0 (2016/09/30) poi-3.12
    700        //      if( val == null || val.isEmpty() ) { oCell.setCellType( CellType.BLANK ); }                             // 6.5.0.0 (2016/09/30) poi-3.15
    701                if( val == null || val.isEmpty() ) { oCell.setBlank(); }                                                                // 7.3.0.0 (2021/01/06) poi-4.1.2
    702
    703        //      switch( oCell.getCellType() ) {                                                                         // 6.5.0.0 (2016/09/30) poi-3.12
    704//              switch( oCell.getCellTypeEnum() ) {                                                                     // 6.5.0.0 (2016/09/30) poi-3.15
    705                switch( oCell.getCellType() ) {                                                                         // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated.
    706        //              case Cell.CELL_TYPE_NUMERIC:                                                                    // 6.5.0.0 (2016/09/30) poi-3.12
    707                        case NUMERIC:                                                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
    708//                                      oCell.setCellValue( Double.valueOf( val ) );
    709                                        oCell.setCellValue( Double.parseDouble( val ) );                // 7.3.0.0 (2021/01/06) SpotBugs 疑わしいﾌﾟﾘﾐﾃｨﾌﾞ値のﾎﾞｸｼﾝｸﾞ
    710                                        break;
    711        //              case Cell.CELL_TYPE_BOOLEAN:                                                                    // 6.5.0.0 (2016/09/30) poi-3.12
    712                        case BOOLEAN:                                                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
    713                                        oCell.setCellValue( "true".equalsIgnoreCase( val ) );
    714                                        break;
    715                        default :
    716                                        oCell.setCellValue( val );
    717                                        break;
    718                }
    719        }
    720
    721        /**
    722         * ｾﾙ値が数字の場合に､数字か日付かを判断して､対応する文字列を返します｡
    723         *
    724         * @og.rev 3.8.5.3 (2006/08/07) 新規追加
    725         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します｡
    726         * @og.rev 6.3.1.0 (2015/06/28) ExcelStyleFormat を使用します｡
    727         *
    728         * @param       oCell EXCELのｾﾙｵﾌﾞｼﾞｪｸﾄ
    729         *
    730         * @return      数字の場合は､文字列に変換した結果を､日付の場合は､"yyyyMMddHHmmss" 形式で返します｡
    731         */
    732        public static String getNumericTypeString( final Cell oCell ) {
    733                final String strText ;
    734
    735                final double dd = oCell.getNumericCellValue() ;
    736                if( DateUtil.isCellDateFormatted( oCell ) ) {
    737        //              strText = DateSet.getDate( DateUtil.getJavaDate( dd ).getTime() , "yyyyMMddHHmmss" );   // 5.5.7.2 (2012/10/09) HybsDateUtil を利用
    738                        strText = ExcelStyleFormat.dateFormat( dd );
    739                }
    740                else {
    741        //              final NumberFormat numFormat = NumberFormat.getInstance();
    742        //              if( numFormat instanceof DecimalFormat ) {
    743        //                      ((DecimalFormat)numFormat).applyPattern( "#.####" );
    744        //              }
    745        //              strText = numFormat.format( dd );
    746                        final String fmrs = oCell.getCellStyle().getDataFormatString();
    747                        strText = ExcelStyleFormat.getNumberValue( fmrs,dd );
    748                }
    749                return strText ;
    750        }
    751
    752        /**
    753         * 全てのSheetに対して､autoSizeColumn設定を行います｡
    754         *
    755         * 重たい処理なので､ﾌｧｲﾙの書き出し直前に一度だけ実行するのがよいでしょう｡
    756         * autoSize設定で､ｶﾗﾑ幅が大きすぎる場合､現状では､
    757         * 初期ｶﾗﾑ幅のmaxColCount倍を限度に設定します｡
    758         * ただし､maxColCount がﾏｲﾅｽの場合は､無制限になります｡
    759         *
    760         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    761         * @og.rev 6.8.2.4 (2017/11/20) rowObj のnull対策(poi-3.17)
    762         *
    763         * @param       wkbook          処理対象のWorkbook
    764         * @param       maxColCount     最大幅を標準ｾﾙ幅の何倍にするかを指定｡ﾏｲﾅｽの場合は､無制限
    765         * @param       dataStRow       ﾃﾞｰﾀ行の開始位置｡未設定時は､-1
    766         */
    767        public static void autoCellSize( final Workbook wkbook , final int maxColCount , final int dataStRow ) {
    768                final int shCnt = wkbook.getNumberOfSheets();
    769
    770                for( int shNo=0; shNo<shCnt; shNo++ ) {
    771                        final Sheet sht = wkbook.getSheetAt( shNo );
    772                        final int defW = sht.getDefaultColumnWidth();           // 標準ｶﾗﾑの文字数
    773                        final int maxWidth = defW*256*maxColCount ;                     // Widthは､文字数(文字幅)*256*最大ｾﾙ数
    774
    775                        int stR = sht.getFirstRowNum();
    776                        final int edR = sht.getLastRowNum();
    777
    778                        // 6.8.2.4 (2017/11/20) rowObj のnull対策(poi-3.17)
    779                        // poi-3.15 でも同じ現象が出ていますが､sht.getRow( stR ) で､Rowｵﾌﾞｼﾞｪｸﾄにnullが返ってきます｡
    780                        // なんとなく､最後の行だけ､返ってきている感じです｡
    781                        // 頻繁には使わないと思いますので､最大ｶﾗﾑ数=256 として処理します｡
    782
    783                        final Row rowObj = sht.getRow( stR );
    784        //              Row rowObj = sht.getRow( stR );
    785        //              if( rowObj == null ) {
    786        //                      for( int i=stR+1; i<edR; i++ ) {
    787        //                              rowObj = sht.getRow( i );
    788        //                              if( rowObj != null ) { break; }
    789        //                      }
    790        //              }
    791
    792                        final int stC = rowObj == null ? 0   : rowObj.getFirstCellNum();        // 6.8.2.4 (2017/11/20) rowObj のnull対策
    793                        final int edC = rowObj == null ? 256 : rowObj.getLastCellNum();         // 含まない (xlsxでは､最大 16,384 列
    794
    795                        // SheetUtil を使用して､計算範囲を指定します｡
    796                        if( stR < dataStRow ) { stR = dataStRow; }              // 計算範囲
    797                        for( int colNo=stC; colNo<edC; colNo++ ) {
    798                                final double wpx = SheetUtil.getColumnWidth( sht,colNo,true,stR,edR );
    799                                if( wpx >= 0.0d ) {                                                     // Cellがないと､ﾏｲﾅｽ値が戻る｡
    800                                        int wd = (int)Math.ceil(wpx * 256) ;
    801                                        if( maxWidth >= 0 && wd > maxWidth ) { wd = maxWidth; } // 最大値が有効な場合は､置き換える
    802                                        sht.setColumnWidth( colNo,wd );
    803                                }
    804                        }
    805
    806                        // Sheet#autoSizeColumn(int) を使用して､自動計算させる場合｡
    807        //              for( int colNo=stC; colNo<edC; colNo++ ) {
    808        //                      sht.autoSizeColumn( colNo );
    809        //                      if( maxWidth >= 0 ) {                                   // 最大値が有効な場合は､置き換える
    810        //                              int wd = sht.getColumnWidth( colNo );
    811        //                              if( wd > maxWidth ) { sht.setColumnWidth( colNo,maxWidth ); }
    812        //                      }
    813        //              }
    814                }
    815        }
    816
    817        /**
    818         * 指定の Workbook の全Sheetを対象に､実際の有効行と有効ｶﾗﾑを取得します｡
    819         *
    820         * ※ 現在､唯一LibreOfficeでのみ､xslx 変換できますが､有効行とｶﾗﾑが
    821         *    ｼｭﾘﾝｸされず､無駄な行とｶﾗﾑが存在します｡
    822         *    これは､xsl で出力されたﾌｧｲﾙから有効な値を取得して､xslxに適用させるための
    823         *    機能で､本来きちんとした有効範囲の xslx が生成されれば､不要な処理です｡
    824         *
    825         * 配列は､[0]=行の最大値(Sheet#getLastRowNum())と､[1]は有効行の中の列の
    826         * 最大値(Row#getLastCellNum())を､ｼｰﾄごとにListに追加していきます｡
    827         *
    828         * @og.rev 8.0.1.0 (2021/10/29) 全Sheetを対象に､実際の有効行と有効ｶﾗﾑを取得
    829         *
    830         * @param       wkbook          処理対象のWorkbook
    831         * @return      ｼｰﾄごとの有効行の配列ﾘｽﾄ
    832         * @see         #activeWorkbook( Workbook,List<int[]> )
    833         */
    834        public static List<int[]> getLastRowCellNum( final Workbook wkbook ) {
    835                final List<int[]> rcList = new ArrayList<>();                                   // ｼｰﾄごとの有効行の配列ﾘｽﾄ
    836
    837                final int shCnt = wkbook.getNumberOfSheets();
    838                for( int shNo=0; shNo<shCnt; shNo++ ) {
    839                        final Sheet sht = wkbook.getSheetAt( shNo );
    840                        final int stR = sht.getFirstRowNum();
    841                        final int edR = sht.getLastRowNum();
    842                        int lastNo = 0;                                                                                         // 行の有効最大値
    843                        for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) {         // 逆順に処理します｡
    844                                final Row rowObj = sht.getRow( rowNo );
    845                                if( rowObj != null ) {
    846                                        final int edC = rowObj.getLastCellNum();                        // 列の有効最大値
    847                                        if( lastNo < edC ) { lastNo = edC; }                            // ｼｰﾄ内での列の最大有効値
    848                                }
    849                        }
    850                        rcList.add( new int[] {edR,lastNo} );                                           // 有効行の配列
    851                }
    852                return rcList;
    853        }
    854
    855        /**
    856         * 指定の Workbook の全Sheetを対象に､空行を取り除き､全体をｼｭﾘﾝｸします｡
    857         *
    858         * この処理は､#saveFile( String ) の直前に行うのがよいでしょう｡
    859         *
    860         * ここでは､Row を逆順にｽｷｬﾝし､Cellが 存在しない間は､行を削除します｡
    861         * 途中の空行の削除ではなく､最終行からの連続した空行の削除です｡
    862         *
    863         * isCellDel=true を指定すると､Cellの末尾削除を行います｡
    864         * 有効行の最後のCellから空ｾﾙを削除していきます｡
    865         * 表形式などの場合は､Cellのあるなしで､ﾚｲｱｳﾄが崩れる場合がありますので
    866         * 処理が不要な場合は､isCellDel=false を指定してください｡
    867         *
    868         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    869         * @og.rev 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます｡
    870         * @og.rev 6.0.2.5 (2014/10/31) Cellの開始､終了番号が､ﾏｲﾅｽのｹｰｽの対応
    871         * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX)
    872         * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0)
    873         * @og.rev 8.0.1.0 (2021/10/29) CellStyle は not null になったための修正
    874         *
    875         * @param       wkbook          処理対象のWorkbook
    876         * @param       isCellDel       Cellの末尾削除を行うかどうか(true:行う/false:行わない)
    877         * @see         #activeWorkbook( Workbook,List<int[]> )
    878         */
    879        public static void activeWorkbook( final Workbook wkbook , final boolean isCellDel ) {
    880                final int shCnt = wkbook.getNumberOfSheets();
    881                for( int shNo=0; shNo<shCnt; shNo++ ) {
    882                        final Sheet sht = wkbook.getSheetAt( shNo );
    883
    884                        final int stR = sht.getFirstRowNum();
    885                        final int edR = sht.getLastRowNum();
    886
    887                        boolean isRowDel = true;                                                                                        // 行の削除は､Cellが見つかるまで｡
    888                        for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) {                         // 逆順に処理します｡
    889                                final Row rowObj = sht.getRow( rowNo );
    890                                if( rowObj != null ) {
    891                                        final int stC = rowObj.getFirstCellNum();
    892                                        final int edC = rowObj.getLastCellNum();
    893                                        // 8.0.1.0 (2021/10/29) 各行の最初のｾﾙｽﾀｲﾙをﾍﾞｰｽとして比較する｡
    894
    895                                        final CellStyle baseCellStyle = rowObj.getCell( stC ).getCellStyle();   // nullﾁｪｯｸ入れてない…
    896                                        for( int colNo=edC; colNo>=stC && colNo>=0; colNo-- ) {         // 6.0.2.5 (2014/10/31) stC,edC が､ﾏｲﾅｽのｹｰｽがある｡
    897                                                final Cell colObj = rowObj.getCell( colNo );
    898                                                if( colObj != null ) {
    899                                                        final String val = getValue( colObj );
    900                                                        if( colObj.getCellType() != CellType.BLANK && val != null && val.length() > 0 ) {                       // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated.
    901                                                                isRowDel = false;                                       // 一つでも現れれば､行の削除は中止
    902                                                                break;
    903                                                        }
    904                                                        // 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます｡
    905                                                        // 8.0.1.0 (2021/10/29) 各行の最初のｾﾙｽﾀｲﾙをﾍﾞｰｽとして比較する｡
    906        //                                              else if( colObj.getCellStyle() != null ) {
    907                                                        else if( ! baseCellStyle.equals(colObj.getCellStyle()) ) {
    908                                                                isRowDel = false;                                       // 一つでも現れれば､行の削除は中止
    909                                                                break;
    910                                                        }
    911                                                        else if( isCellDel ) {
    912                                                                rowObj.removeCell( colObj );            // CELL_TYPE_BLANK の場合は､削除
    913                                                        }
    914                                                }
    915                                        }
    916                                        if( isRowDel ) { sht.removeRow( rowObj );       }
    917                                        else if( !isCellDel ) { break; }                                // Cell の末尾削除を行わない場合は､break すればよい｡
    918                                }
    919                        }
    920                }
    921        }
    922
    923        /**
    924         * 指定の Workbook の全Sheetを対象に､実際の有効行と有効ｶﾗﾑを元に全体をｼｭﾘﾝｸします｡
    925         *
    926         * ※ 現在､唯一LibreOfficeでのみ､xslx 変換できますが､有効行とｶﾗﾑが
    927         *    ｼｭﾘﾝｸされず､無駄な行とｶﾗﾑが存在します｡
    928         *    これは､xsl で出力されたﾌｧｲﾙから有効な値を取得して､xslxに適用させるための
    929         *    機能で､本来きちんとした有効範囲の xslx が生成されれば､不要な処理です｡
    930         *
    931         * 引数のListｵﾌﾞｼﾞｪｸﾄに従って､無条件に処理を行います｡
    932         *
    933         * @og.rev 8.0.1.0 (2021/10/29) 全Sheetを対象に､実際の有効行と有効ｶﾗﾑを取得
    934         *
    935         * @param       wkbook          処理対象のWorkbook
    936         * @param       rcList          ｼｰﾄごとの有効行の配列ﾘｽﾄ
    937         * @see         #getLastRowCellNum( Workbook )
    938         * @see         #activeWorkbook( Workbook,boolean )
    939         */
    940        public static void activeWorkbook( final Workbook wkbook , final List<int[]> rcList ) {
    941                final int shCnt = wkbook.getNumberOfSheets();
    942                for( int shNo=0; shNo<shCnt; shNo++ ) {
    943                        final Sheet sht = wkbook.getSheetAt( shNo );
    944                        final int[] rowcol = rcList.get(shNo);                                          // ｼｰﾄ内の有効行と列
    945
    946                        final int stR = rowcol[0];
    947                        final int edR = sht.getLastRowNum();                                            // 参考程度
    948
    949                        // edR～stRまでの行は､無条件に削除します｡
    950                        for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) {         // 逆順に処理します｡
    951                                final Row rowObj = sht.getRow( rowNo );
    952                                if( rowObj != null ) {
    953                                        sht.removeRow( rowObj );
    954                                }
    955                        }
    956
    957                        // stR～0までの行は､有効行なので､ｶﾗﾑの処理を考えます｡
    958                        final int stC = rowcol[1];                                                                      // ｼｰﾄの中での有効ｶﾗﾑの最大値
    959                        for( int rowNo=stR; rowNo>=0; rowNo-- ) {                                       // 逆順に処理します｡
    960                                final Row rowObj = sht.getRow( rowNo );
    961                                if( rowObj != null ) {
    962                                        final int edC = rowObj.getLastCellNum();                        // 参考程度
    963                                        // edC～stCまでのｶﾗﾑは､無条件に削除します｡
    964                                        for( int colNo=edC; colNo>=stC && colNo>=0; colNo-- ) {         // 6.0.2.5 (2014/10/31) stC,edC が､ﾏｲﾅｽのｹｰｽがある｡
    965                                                final Cell colObj = rowObj.getCell( colNo );
    966                                                if( colObj != null ) {
    967                                                        rowObj.removeCell( colObj );
    968                                                }
    969                                        }
    970                                }
    971                        }
    972                }
    973        }
    974
    975        /**
    976         * ﾌｧｲﾙから､Workbookｵﾌﾞｼﾞｪｸﾄを新規に作成します｡
    977         *
    978         * @og.rev 6.0.2.3 (2014/10/10) 新規作成
    979         * @og.rev 6.2.0.0 (2015/02/27) ﾌｧｲﾙ引数を､String → File に変更
    980         * @og.rev 7.0.0.0 (2018/10/01) poi-4.0.0 例外InvalidFormatExceptionは対応するtry文の本体ではｽﾛｰされません
    981         *
    982         * @param       file    入力ﾌｧｲﾙ
    983         * @return      Workbookｵﾌﾞｼﾞｪｸﾄ
    984         * @og.rtnNotNull
    985         */
    986        public static Workbook createWorkbook( final File file ) {
    987                InputStream fis = null;
    988                try {
    989                        // File ｵﾌﾞｼﾞｪｸﾄでcreate すると､ﾌｧｲﾙがｵｰﾌﾟﾝされたままになってしまう｡
    990                        fis = new BufferedInputStream( new FileInputStream( file ) );
    991                        return WorkbookFactory.create( fis );
    992                }
    993                catch( final IOException ex ) {
    994                        final String errMsg = "ﾌｧｲﾙ読込みｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    995                        throw new OgRuntimeException( errMsg,ex );
    996                }
    997                // 7.0.0.0 (2018/10/01) poi-4.0.0 対応するtry文の本体ではｽﾛｰされません
    998//              catch( final InvalidFormatException ex ) {
    999//                      final String errMsg = "ﾌｧｲﾙ形式ｴﾗｰ[" + file + "]" + CR + ex.getMessage() ;
    1000//                      throw new OgRuntimeException( errMsg,ex );
    1001//              }
    1002                finally {
    1003                        Closer.ioClose( fis );
    1004                }
    1005        }
    1006
    1007        /**
    1008         * ｼｰﾄ一覧を､Workbook から取得します｡
    1009         *
    1010         * 取得元が､Workbook なので､xls , xlsx どちらの形式でも取り出せます｡
    1011         *
    1012         * EXCEL上のｼｰﾄ名を､配列で返します｡
    1013         *
    1014         * @og.rev 6.0.2.3 (2014/10/10) 新規作成
    1015         *
    1016         * @param       wkbook Workbookｵﾌﾞｼﾞｪｸﾄ
    1017         * @return      ｼｰﾄ名の配列
    1018         */
    1019        public static String[] getSheetNames( final Workbook wkbook ) {
    1020                final int shCnt = wkbook.getNumberOfSheets();
    1021
    1022                String[] shtNms = new String[shCnt];
    1023
    1024                for( int i=0; i<shCnt; i++ ) {
    1025                        final Sheet sht = wkbook.getSheetAt( i );
    1026                        shtNms[i] = sht.getSheetName();
    1027                }
    1028
    1029                return shtNms;
    1030        }
    1031
    1032        /**
    1033         * 名前定義一覧を取得します｡
    1034         *
    1035         * EXCEL上に定義された名前を､配列で返します｡
    1036         * ここでは､名前とFormulaをﾀﾌﾞで連結した文字列を配列で返します｡
    1037         * Name ｵﾌﾞｼﾞｪｸﾄを削除すると､EXCELが開かなくなったりするので､
    1038         * 取りあえず一覧を作成して､手動で削除してください｡
    1039         * なお､名前定義には､非表示というのがありますので､ご注意ください｡
    1040         *
    1041         * ◆ 非表示になっている名前の定義を表示にする EXCEL VBA ﾏｸﾛ
    1042         * http://dev.classmethod.jp/tool/excel-delete-name/
    1043         *    Sub VisibleNames()
    1044         *        Dim name
    1045         *        For Each name In ActiveWorkbook.Names
    1046         *            If name.Visible = False Then
    1047         *                name.Visible = True
    1048         *            End If
    1049         *        Next
    1050         *        MsgBox "すべての名前の定義を表示しました｡", vbOKOnly
    1051         *    End Sub
    1052         *
    1053         * ※ EXCEL2010 数式ﾀﾌﾞ→名前の管理 で､複数選択で､削除できます｡
    1054         *    ただし､非表示の名前定義は､先に表示しないと､削除できません｡
    1055         * ◆ 名前の一括削除 EXCEL VBA ﾏｸﾛ
    1056         * http://komitsudo.blog70.fc2.com/blog-entry-104.html
    1057         *    Sub DeleteNames()
    1058         *        Dim name
    1059         *        On Error Resume Next
    1060         *        For Each name In ActiveWorkbook.Names
    1061         *            If Not name.BuiltIn Then
    1062         *                name.Delete
    1063         *            End If
    1064         *        Next
    1065         *        MsgBox "すべての名前の定義を削除しました｡", vbOKOnly
    1066         *    End Sub
    1067         *
    1068         * @og.rev 6.0.2.3 (2014/10/10) 新規作成
    1069         * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] WorkbookのgetNameAt(int)は推奨されません (POI4.0.0)
    1070         *
    1071         * @param       wkbook Workbookｵﾌﾞｼﾞｪｸﾄ
    1072         * @return      名前定義(名前+TAB+Formula)の配列
    1073         * @og.rtnNotNull
    1074         */
    1075        public static String[] getNames( final Workbook wkbook ) {
    1076//              final int cnt = wkbook.getNumberOfNames();
    1077
    1078                final Set<String> nmSet = new TreeSet<>();
    1079
    1080                // 7.0.0.0 (2018/10/01) 警告:[deprecation] WorkbookのgetNameAt(int)は推奨されません (POI4.0.0)
    1081//              for( int i=0; i<cnt; i++ ) {
    1082                for( final Name nm : wkbook.getAllNames() ) {
    1083                        String name     = null;
    1084                        String ref      = null;
    1085
    1086//                      final Name nm = wkbook.getNameAt(i);
    1087                        try {
    1088                                name = nm.getNameName();
    1089                                ref  = nm.getRefersToFormula();
    1090                        }
    1091        //              catch( final Exception ex ) {                                   // 6.1.0.0 (2014/12/26) refactoring
    1092                        catch( final RuntimeException ex ) {
    1093                                final String errMsg = "POIUtil:RefersToFormula Error! name=[" + name + "]" + ex.getMessage() ;
    1094                                System.out.println( errMsg );
    1095                                // Excel97形式の場合､getRefersToFormula() でｴﾗｰが発生することがある｡
    1096                        }
    1097
    1098                        nmSet.add( name + "\t" + ref );
    1099
    1100                        // 削除するとEXCELが壊れる？ なお､削除時には逆順で廻さないとｱﾄﾞﾚｽがずれます｡
    1101                        // if( nm.isDeleted() ) { wkbook.removeName(i); }
    1102                }
    1103
    1104                return nmSet.toArray( new String[nmSet.size()] );
    1105        }
    1106
    1107        /**
    1108         * 書式のｽﾀｲﾙ一覧を取得します｡
    1109         *
    1110         * EXCEL上に定義された書式のｽﾀｲﾙを､配列で返します｡
    1111         * 書式のｽﾀｲﾙの名称は､CellStyle にﾒｿｯﾄﾞが定義されていません｡
    1112         * 実ｸﾗｽである HSSFCellStyle にｷｬｽﾄして使用する
    1113         * 必要があります｡(XSSFCellStyle にも名称を取得するﾒｿｯﾄﾞがありません｡)
    1114         *
    1115         * ※ EXCEL2010 ﾎｰﾑﾀﾌﾞ→ｾﾙのｽﾀｲﾙ は､一つづつしか削除できません｡
    1116         *    ﾏｸﾛは､開発ﾀﾌﾞ→Visual Basic で､挿入→標準ﾓｼﾞｭｰﾙ を開き
    1117         *    ﾃｷｽﾄを張り付けてください｡
    1118         *    実行は､開発ﾀﾌﾞ→ﾏｸﾛ で､ﾏｸﾛ名を選択して､実行します｡
    1119         *    最後は､削除してください｡
    1120         *
    1121         * ◆ ｽﾀｲﾙの一括削除 EXCEL VBA ﾏｸﾛ
    1122         * http://komitsudo.blog70.fc2.com/blog-entry-104.html
    1123         *    Sub DeleteStyle()
    1124         *        Dim styl
    1125         *        On Error Resume Next
    1126         *        For Each styl In ActiveWorkbook.Styles
    1127         *            If Not styl.BuiltIn Then
    1128         *                styl.Delete
    1129         *            End If
    1130         *        Next
    1131         *        MsgBox "すべての追加ｽﾀｲﾙを削除しました｡", vbOKOnly
    1132         *    End Sub
    1133         *
    1134         * ◆ 名前の表示､削除､ｽﾀｲﾙの削除の一括実行 EXCEL VBA ﾏｸﾛ
    1135         *    Sub AllDelete()
    1136         *        Call VisibleNames
    1137         *        Call DeleteNames
    1138         *        Call DeleteStyle
    1139         *        MsgBox "すべての処理を完了しました｡", vbOKOnly
    1140         *    End Sub
    1141         *
    1142         * @og.rev 6.0.2.3 (2014/10/10) 新規作成
    1143         *
    1144         * @param       wkbook Workbookｵﾌﾞｼﾞｪｸﾄ
    1145         * @return      書式のｽﾀｲﾙ一覧
    1146         * @og.rtnNotNull
    1147         */
    1148        public static String[] getStyleNames( final Workbook wkbook ) {
    1149                final int cnt = wkbook.getNumCellStyles();              // return 値は､short
    1150
    1151                final Set<String> nmSet = new TreeSet<>();
    1152
    1153                for( int s=0; s<cnt; s++ ) {
    1154                        final CellStyle cs = wkbook.getCellStyleAt( (short)s );
    1155                        if( cs instanceof HSSFCellStyle ) {
    1156                                final HSSFCellStyle hcs = (HSSFCellStyle)cs;
    1157                                final String name = hcs.getUserStyleName();
    1158                                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    1159                                if( name == null ) {                                                    // この処理は不要かも｡
    1160                                        final HSSFCellStyle pst = hcs.getParentStyle();
    1161                                        if( pst != null ) {
    1162                                                final String pname = pst.getUserStyleName();
    1163                                                if( pname != null ) { nmSet.add( pname ); }
    1164                                        }
    1165                                }
    1166                                else {
    1167                                        nmSet.add( name );
    1168                                }
    1169                        }
    1170                }
    1171
    1172                return nmSet.toArray( new String[nmSet.size()] );
    1173        }
    1174
    1175        /**
    1176         * ｾﾙ情報を返します｡
    1177         *
    1178         * ｴﾗｰ発生時に､どのｾﾙでｴﾗｰが発生したかの情報を取得できるようにします｡
    1179         *
    1180         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    1181         * @og.rev 6.0.3.0 (2014/11/13) ｾﾙ情報を作成する時に､値もｾｯﾄします｡
    1182         * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるﾛｼﾞｯｸ変更
    1183         * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします｡
    1184         *
    1185         * @param       oCell EXCELのｾﾙｵﾌﾞｼﾞｪｸﾄ
    1186         * @return      ｾﾙ情報の文字列
    1187         */
    1188        public static String getCellMsg( final Cell oCell ) {
    1189                String lastMsg = null;
    1190
    1191                if( oCell != null ) {
    1192                        final String shtNm = oCell.getSheet().getSheetName();
    1193                        final int  rowNo = oCell.getRowIndex();
    1194                        final int  celNo = oCell.getColumnIndex();
    1195
    1196                        // 6.0.3.0 (2014/11/13) ｾﾙ情報を作成する時に､値もｾｯﾄします｡
    1197                        lastMsg = " Sheet=" + shtNm + ", Row=" + rowNo + ", Cel=" + celNo
    1198                                                 + "(" + getCelKigo(rowNo,celNo) + ") , Val=" + oCell.toString() ;
    1199                }
    1200
    1201                return lastMsg;
    1202        }
    1203
    1204        /**
    1205         * Excelの行番号,列番号より､ｾﾙ記号を求めます｡
    1206         *
    1207         * 行番号は､0から始まる数字ですが､記号化する場合は､１から始まります｡
    1208         * Excelの列記号とは､A,B,C,…,Z,AA,AB,…,ZZ,AAA,AAB,… と続きます｡
    1209         * つまり､ｱﾙﾌｧﾍﾞｯﾄだけの､２６進数になります｡(ｾﾞﾛの扱いが少し特殊です)
    1210         * 列番号は､0から始まる数字で､0=A,1=B,2=C,…,25=Z,26=AA,27=AB,…,701=ZZ,702=AAA,703=AAB,…
    1211         * EXCELの行列記号にする場合は､この列記号に､行番号を､＋１して付ければよいだけです｡
    1212         * (※ 列番号に＋１するのは､内部では０から始まる列番号ですが､表示上は１から始まります)
    1213         *
    1214         * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるﾛｼﾞｯｸ変更
    1215         * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします｡
    1216         *
    1217         * @param       rowNo   行番号(0,1,2,…)
    1218         * @param       colNo   列番号(0,1,2,…)
    1219         * @return      Excelの列記号(A1,B2,C3,…)
    1220         */
    1221        public static String getCelKigo( final int rowNo,final int colNo ) {
    1222                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    1223                int cnt = colNo;
    1224                while( cnt >= 26 ) {
    1225                        buf.append( (char)('A'+cnt%26) );
    1226                        cnt = cnt/26-1;
    1227                }
    1228                buf.append( (char)('A'+cnt%26) )
    1229                        .reverse()                                                              // append で逆順に付けているので､反転して返します｡
    1230                        .append( rowNo+1 );
    1231
    1232                return buf.toString();
    1233        }
    1234
    1235        /**
    1236         * ｱﾌﾟﾘｹｰｼｮﾝのｻﾝﾌﾟﾙです｡
    1237         *
    1238         * 入力ﾌｧｲﾙ名 は必須で､第一引数固定です｡
    1239         * 第二引数は､処理方法で､-ALL か､-LINE を指定します｡何も指定しなければ､-ALL です｡
    1240         * 第三引数を指定した場合は､Encode を指定します｡
    1241         *
    1242         * Usage: java org.opengion.fukurou.model.POIUtil 入力ﾌｧｲﾙ名 [処理方式] [ｴﾝｺｰﾄﾞ]
    1243         *   -A(LL)        ･･･ ALL 一括処理(初期値)
    1244         *   -L(INE)       ･･･ LINE 行単位処理
    1245         *   -S(heet)      ･･･ Sheet名一覧
    1246         *   -N(AME)       ･･･ NAME:名前定義
    1247         *   -C(ellStyle)  ･･･ CellStyle:書式のｽﾀｲﾙ
    1248         *
    1249         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    1250         * @og.rev 6.2.3.0 (2015/05/01) ﾊﾟﾗﾒｰﾀ変更､textReader → extractor に変更
    1251         * @og.rev 6.2.4.2 (2015/05/29) 引数判定の true/false の処理が逆でした｡
    1252         * @og.rev 6.3.9.0 (2015/11/06) Java 8 ﾗﾑﾀﾞ式に変更
    1253         *
    1254         * @param       args    ｺﾏﾝﾄﾞ引数配列
    1255         */
    1256        public static void main( final String[] args ) {
    1257                final String usageMsg = "Usage: java org.opengion.fukurou.model.POIUtil 入力ﾌｧｲﾙ名 [処理方式] [ｴﾝｺｰﾄﾞ]" + "\n" +
    1258                                                                "\t -A(LL)        ･･･ ALL 一括処理(初期値)      \n" +
    1259                                                                "\t -L(INE)       ･･･ LINE 行単位処理           \n" +
    1260                                                                "\t -S(heet)      ･･･ Sheet名一覧               \n" +
    1261                                                                "\t -N(AME)       ･･･ NAME:名前定義             \n" +
    1262                                                                "\t -C(ellStyle)  ･･･ CellStyle:書式のｽﾀｲﾙ  \n" ;
    1263                if( args.length == 0 ) {
    1264                        System.err.println( usageMsg );
    1265                        return ;
    1266                }
    1267
    1268                final File file = new File( args[0] );
    1269                final char type     = args.length >= 2 ? args[1].charAt(1) : 'A' ;
    1270                final String encode = args.length >= 3 ? args[2] : null ;                               // 6.2.4.2 (2015/05/29) true/false の処理が逆でした｡
    1271
    1272                switch( type ) {
    1273                        case 'A' :  if( encode == null ) {
    1274                                                        System.out.println( POIUtil.extractor( file ) );
    1275                                                }
    1276                                                else {
    1277                                                        System.out.println( POIUtil.extractor( file,encode ) );
    1278                                                }
    1279                                                break;
    1280                        // 6.3.9.0 (2015/11/06) Java 8 ﾗﾑﾀﾞ式に変更
    1281                        case 'L' : final TextConverter<String,String> conv =
    1282                                                                ( val,cmnt ) -> {
    1283                                                                        System.out.println( "val=" + val + " , cmnt=" + cmnt );
    1284                                                                        return null;
    1285                                                                };
    1286
    1287                                        //              new TextConverter<String,String>() {
    1288                                        //                      /**
    1289                                        //                       * 入力文字列を､変換します｡
    1290                                        //                       *
    1291                                        //                       * @param       val  入力文字列
    1292                                        //                       * @param       cmnt ｺﾒﾝﾄ
    1293                                        //                       * @return      変換文字列(変換されない場合は､null)
    1294                                        //                       */
    1295                                        //                      @Override
    1296                                        //                      public String change( final String val , final String cmnt ) {
    1297                                        //                              System.out.println( "val=" + val + " , cmnt=" + cmnt );
    1298                                        //                              return null;
    1299                                        //                      }
    1300                                        //              };
    1301
    1302                                                if( encode == null ) {
    1303                                                        POIUtil.textReader( file,conv );
    1304                                                }
    1305                                                else {
    1306                                                        POIUtil.textReader( file,conv,encode );
    1307                                                }
    1308                                                break;
    1309                        case 'S' :  final String[] shts = POIUtil.getSheetNames( POIUtil.createWorkbook(file) );
    1310                                                System.out.println( "No:\tSheetName" );
    1311                                                for( int i=0; i<shts.length; i++ ) {
    1312                                                        System.out.println( i + "\t" + shts[i] );
    1313                                                }
    1314                                                break;
    1315                        case 'N' :  final String[] nms = POIUtil.getNames( POIUtil.createWorkbook(file) );
    1316                                                System.out.println( "No:\tName\tFormula" );
    1317                                                for( int i=0; i<nms.length; i++ ) {
    1318                                                        System.out.println( i + "\t" + nms[i] );
    1319                                                }
    1320                                                break;
    1321                        case 'C' :  final String[] sns = POIUtil.getStyleNames( POIUtil.createWorkbook(file) );
    1322                                                System.out.println( "No:\tStyleName" );
    1323                                                for( int i=0; i<sns.length; i++ ) {
    1324                                                        System.out.println( i + "\t" + sns[i] );
    1325                                                }
    1326                                                break;
    1327                        default :   System.err.println( usageMsg );
    1328                                                break;
    1329                }
    1330        }
    1331}