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