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