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