    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.plugin.io;
    017
    018import java.io.PrintWriter;
    019import java.io.File;                                                                            // 6.2.0.0 (2015/02/27)
    020
    021import org.opengion.fukurou.model.NativeType;
    022import org.opengion.fukurou.util.StringUtil;
    023import org.opengion.hayabusa.common.HybsSystemException;
    024import org.opengion.hayabusa.db.DBTableModel;
    025import org.opengion.hayabusa.db.DBColumn;
    026
    027import org.opengion.fukurou.model.ExcelModel;
    028
    029/**
    030 * ネイティブEXCELファイルの書き出しクラスです。
    031 *
    032 * DefaultTableWriter を継承していますので，ラベル，名前，データの出力部のみ
    033 * オーバーライドして，MIcrosoft Excelファイルの出力機能を実現しています。
    034 *
    035 * 出力形式は、openXML形式にも対応しています。
    036 * 出力ファイルの拡張子が、.xlsならExcel2003のバイナリ形式、.xlsxならExcel2007の
    037 * openXML形式で出力されます。
    038 *
    039 * useCellStyle      = [false/true];  データを書き込んだ範囲に罫線を入れる(true)かどうか(初期値:false)
    040 * useAutoCellSize   = [false/true];  セルの幅をデータの幅に自動的に合わせる(true)かどうか(初期値:false)
    041 * useActiveWorkbook = [false/true];  セルの有効範囲を設定する(true)かどうか(初期値:false)
    042 * pageBreakColumn   = PBCLM;         シートブレイクするキーとなるカラム名を指定します(このカラムの値がシート名になります)
    043 * fileBreakColumn   = FBCLM;         ファイルブレイクするキーとなるカラム名を指定します(このカラムの値がファイル名になります)
    044 *
    045 * appendモードの場合は、オリジナルが雛形ファイルになります。雛形ファイルを指定した場合は、
    046 * 同じファイル名で無ければエラーになります。
    047 *
    048 * @og.group ファイル出力
    049 *
    050 * @og.rev 4.3.4.3 (2008/12/22) 一部protected化
    051 * @og.rev 4.3.6.7 (2009/05/22) ooxml形式対応
    052 * @og.rev 6.0.1.2 (2014/08/08) ロジックの完全書き換え(保証もできない位･･･)
    053 *
    054 * @version  4.0
    055 * @author       Kazuhiko Hasegawa
    056 * @since    JDK5.0,
    057 */
    058public class TableWriter_Excel extends TableWriter_Default {
    059        /** このプログラムのVERSION文字列を設定します。   {@value} */
    060        private static final String VERSION = "7.2.6.1 (2020/07/17)" ;
    061
    062        private int             nRowIndex               ;                               // 6.0.1.2 (2014/08/08) private化
    063        private String  filename                ;                               // 3.5.4.3 (2004/01/05)
    064        private String  directory               ;                               // 6.0.2.0 (2014/09/19) ディレクトリとファイルを分けて管理
    065        private String  fileKey                 ;                               // 6.0.2.0 (2014/09/19) ディレクトリとファイルを分けて管理
    066        private String  sheetName               = "Sheet";              // 3.5.4.3 (2004/01/05) 書き込むSheet名
    067        private String  fontName                ;                               // 3.8.5.3 (2006/08/07)
    068        private short   fontPoint               = -1;                   // 3.8.5.3 (2006/08/07)
    069        private String  refFilename             ;                               // 3.5.4.3 (2004/01/05)
    070        private String  refSheetName    ;                               // 3.5.4.3 (2004/01/05)
    071        // 5.1.4.0 (2010/03/01) 行番号情報を、出力する(true)/しない(false)を指定
    072        private boolean         useNumber       = true;
    073        private int             skipRowCount    ;                               // 6.0.1.2 (2014/08/08) データの書き込み開始位置設定
    074
    075        private ExcelModel excel                ;                               // 6.0.2.0 (2014/09/19) 新規作成
    076
    077        // 6.0.2.0 (2014/09/19) EXCEL関係機能追加
    078        private boolean useCellStyle            ;                       // データを書き込んだ範囲に罫線を入れるかどうかを指定します
    079        private boolean useAutoCellSize         ;                       // セルの幅をデータの幅に自動的に合わせるかどうかを指定します
    080        private boolean useActiveWorkbook       ;                       // セルの有効範囲を設定するかどうかを指定します
    081        private String  fileBreakColumn         ;                       // ファイルブレイクするキーとなるカラム名を指定します(このカラムの値がファイル名になります)
    082        private String  pageBreakColumn         ;                       // シートブレイクするキーとなるカラム名を指定します(このカラムの値がシート名になります)
    083        private boolean noPageBreak                     ;                       // 6.1.0.0 (2014/12/26) pageBreak,fileBreak 修正
    084        private String  hLinkKeyColumn          ;                       // Hyperlinkを作成するキーとなるカラム名を指定
    085        private String  hLinkValColumn          ;                       // Hyperlinkを作成する値となるカラム名を指定
    086
    087        private String  addTitleSheet           ;                       // Sheet一覧を先頭Sheetに作成する場合のSheet名を指定します
    088
    089        private int             pageBreakClmNo          = -1;           // シートブレイクするキーとなるカラムNo
    090        private int             fileBreakClmNo          = -1;           // ファイルブレイクするキーとなるカラムNo
    091        private int             hLinkKeyClmNo           = -1;           // Hyperlinkを作成するキーとなるカラムNo
    092        private int             hLinkValClmNo           = -1;           // Hyperlinkを作成する値となるカラムNo
    093        private int             tblRow                          ;                       // シートブレイクしても、データは続きから出力します。
    094
    095        private boolean[] nvar                          ;                       // 6.0.2.0 (2014/09/19) writeData( DBTableModel ,PrintWriter ) で使うカラム情報
    096        private boolean[] isNums                        ;                       // 6.0.2.0 (2014/09/19) writeData( DBTableModel ,PrintWriter ) で使うカラム情報
    097
    098        // 5.9.12.1 (2016/09/09) シート上書き設定
    099        private boolean         sheetOverwrite          ;               // 5.9.12.1 (2016/09/09)
    100        private String[]        recalcSheetNames        ;               // 5.9.12.1 (2016/09/09)
    101
    102        /**
    103         * デフォルトコンストラクター
    104         *
    105         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    106         */
    107        public TableWriter_Excel() { super(); }         // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    108
    109        /**
    110         * DBTableModel から 各形式のデータを作成して,PrintWriter に書き出します。
    111         * このメソッドは、EXCEL 書き出し時に使用します。
    112         *
    113         * 雛形シートを使う場合は、雛形シートの後ろに追加していきます。
    114         * 最後には、雛形シートとして存在しな部分は、すべて削除します。
    115         * 雛形シートを使わない場合は、新規シートに追加していくことになります。
    116         * appendモードの場合は、オリジナルが雛形ファイルになります。
    117         * 雛形ファイルの指定は、無効になります。
    118         *
    119         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
    120         * @og.rev 5.1.4.0 (2010/03/01) columns 対応 、useNumber属性対応
    121         * @og.rev 6.0.1.2 (2014/08/08) ロジックの完全書き換え(保証もできない位･･･)
    122         * @og.rev 6.0.2.0 (2014/09/19) EXCEL新機能追加
    123         * @og.rev 6.1.0.0 (2014/12/26) Excel関係改善
    124         * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更
    125         * @og.rev 6.2.2.3 (2015/04/10) 雛形シートにそのままデータを書き込んでいく。シートは削除不要
    126         * @og.rev 5.9.12.1 (2016/09/09) sheetOverwrite,recalcSheetNames
    127         * @og.rev 6.5.0.0 (2016/09/30) セルの計算式の再計算をさせる recalcSheetNames 属性の追加。(5.9.12.1 (2016/09/09))
    128         *
    129         * @see #isExcel()
    130         */
    131        @Override
    132        public void writeDBTable() {
    133                if( ! createDBColumn() ) { return ; }           // 親クラスを改造。２回実行されない。
    134
    135                useNumber = isUseNumber();
    136
    137                // 3.5.6.0 (2004/06/18) 移動
    138                if( filename == null ) {
    139                        final String errMsg = "ファイルが指定されていません。";
    140                        throw new HybsSystemException( errMsg );
    141                }
    142
    143                // appendモードの場合は、オリジナルが雛形ファイルになります。
    144                // 雛形ファイルの指定は、無効になります。
    145                if( isAppend() ) {
    146                        // 互換性のため、雛形ファイルが出力ファイルと同じでない場合はエラーとします。
    147                        if( refFilename != null && !filename.equalsIgnoreCase( refFilename ) ) {
    148                                final String errMsg = "追加(appendモード)の時、雛形ファイルの指定は無効です。" + CR
    149                                                                + " filename=[" + filename + "] , refFilename=[" + refFilename + "]"  + CR ;
    150                                throw new HybsSystemException( errMsg );
    151                        }
    152
    153                        refFilename = filename ;
    154                }
    155
    156                // 雛形があれば、雛形ファイルを読み込みます。なければ、オリジナルのファイルを読み込みます。
    157                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    158                final boolean isRefFile = refFilename != null && refFilename.length() > 0;
    159                final String nameUse = isRefFile ? refFilename : filename ;             // 6.3.9.1 (2015/11/27)
    160
    161                initWriteData();                                                // 6.0.2.0 (2014/09/19) カラム関連の初期化
    162
    163                final boolean isDebug = isDebug();              // 6.1.0.0 (2014/12/26) デバッグ情報
    164
    165                // 6.1.0.0 (2014/12/26) useRenderer 時に使用。できるだけループ内処理を避ける。
    166                final DBColumn fileBreakClm = fileBreakClmNo >= 0 && isUseRenderer() ? dbColumn[fileBreakClmNo] : null ;
    167                final DBColumn pageBreakClm = pageBreakClmNo >= 0 && isUseRenderer() ? dbColumn[pageBreakClmNo] : null ;
    168
    169                // ファイルブレイクの初期値を求めます。(fileKey は、setFilename(String,String) で、初期値セット済み)
    170                if( fileBreakClmNo >= 0 ) { fileKey = table.getValue( tblRow,fileBreakClmNo ); }
    171                // 処理のループを抜ける条件は、すべてのデータを書出し終わった場合。fileKey = null をセットします。
    172                while( fileKey != null ) {
    173                        // fileKey は、super.writeDBTable( null ) で、writeData( DBTableModel ,PrintWriter )で、再セットされる。
    174                        final String fileKey_ren = fileBreakClm == null ? fileKey : fileBreakClm.getWriteValue( fileKey );
    175
    176                        // ExcelModelで作成。新規の場合は、nameUseの拡張子(.xlsx/.xls)に応じて、タイプが変わります。
    177                        excel = new ExcelModel( new File( nameUse ) , isRefFile );              // 6.2.0.0 (2015/02/27)
    178
    179                        // 雛形シートを使わない場合のみ、フォント設定を行う。
    180                        if( !isRefFile ) { excel.setFont( fontName,fontPoint ); }
    181                        // 6.0.2.0 (2014/09/19) 新機能追加
    182                        if( useCellStyle ) { excel.setCellStyle(); }
    183                        excel.useAutoCellSize( useAutoCellSize );
    184                        excel.setRecalcSheetName( recalcSheetNames );                                   // 6.5.0.0 (2016/09/30) セルの計算式の再計算をさせる recalcSheetNames 属性の追加。
    185                        excel.setAddTitleSheet( addTitleSheet );
    186
    187                        if( isRefFile ) { excel.setRefSheetName( refSheetName ); }
    188
    189                        // シートブレイクする場合は、sheetName名をあらかじめ取得する。
    190                        if( pageBreakClmNo >= 0 ) { sheetName = table.getValue( tblRow,pageBreakClmNo ); }
    191                        noPageBreak = true ;    // ファイルブレイク、シートブレイクの場合は、false をセット
    192                        while( noPageBreak ) {
    193                                // 6.1.0.0 (2014/12/26) デバッグ情報
    194                                if( isDebug ) { System.out.println( " file=" + fileKey + " , sheet=" + sheetName + " , row=" + tblRow ) ; }
    195
    196                                // シート名のセット。重複チェックと新しいシート名の作成処理を行う。
    197                                final String sheetName_ren = pageBreakClm == null ? sheetName : pageBreakClm.getWriteValue( sheetName );
    198                //              excel.createSheet( sheetName_ren );
    199                                excel.createSheet( sheetName_ren , sheetOverwrite );
    200
    201                                nRowIndex = skipRowCount;               // 6.0.1.2 (2014/08/08) 開始行の指定
    202
    203                                // この super.writeDBTable(null) 内から、各種実態のメソッドが呼ばれる。
    204                                // シートブレイク,ファイルブレイク は、writeData( DBTableModel ,PrintWriter ) で処理される。
    205                                // シートブレイクすると、新しい sheetName がセットされ、noPageBreak=false がセットされる。
    206                                // ファイルブレイクすると、新しい fileKey がセットされ、noPageBreak=false がセットされる。
    207                                super.writeDBTable( null );
    208
    209                                // 指定以降の余計な行を削除
    210                                excel.removeRow( nRowIndex );
    211                        }
    212
    213                        // 6.0.2.0 (2014/09/19) activeWorkbook は、全シートに対して、最後に適用する。
    214                        if( useActiveWorkbook ) { excel.activeWorkbook( false ); }              // false は Cellの末尾削除は行わない。
    215
    216                        // メモリ中のデータをファイルに書き込む
    217                        excel.saveFile( new File( directory,fileKey_ren )  );                   // 6.2.0.0 (2015/02/27)
    218                }
    219        }
    220
    221        /**
    222         * PrintWriter に DBTableModelのヘッダー情報を書き込みます。
    223         * ここでは、ラベル情報を同じデータを出力します。
    224         *
    225         * @og.rev 7.2.6.1 (2020/07/17) H:Header(ヘッダー)属性追加。
    226         *
    227         * @param       table  DBTableModelオブジェクト
    228         * @param       writer PrintWriterオブジェクト
    229         */
    230        @Override
    231        protected void writeLabel2( final DBTableModel table,final PrintWriter writer ) {
    232                writeLabel( table,writer );
    233        }
    234
    235        /**
    236         * PrintWriter に DBTableModelのラベル情報を書き込みます。
    237         * 第一カラム目は、ラベル情報を示す "#Label" を書き込みます。
    238         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    239         *
    240         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    241         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    242         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    243         * @og.rev 6.2.4.2 (2015/05/29) StringUtil#tagCut(String) をラベルに適用します。
    244         *
    245         * @param       table  DBTableModelオブジェクト
    246         * @param       writer PrintWriterオブジェクト
    247         */
    248        @Override
    249        protected void writeLabel( final DBTableModel table,final PrintWriter writer ) {
    250                short nColIndex = 0;
    251                excel.createRow( nRowIndex++ );
    252                if( useNumber ) { headerCellValue( "Label",nColIndex++ ); }
    253
    254                for( int i=0; i<numberOfColumns; i++ ) {
    255                        final int clm = clmNo[i];
    256                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    257                                nColIndex++;
    258                                continue;
    259                        }
    260
    261                        final String val = StringUtil.tagCut( dbColumn[clm].getLabel() );               // 6.2.4.2 (2015/05/29)
    262                        headerCellValue( val,nColIndex++ );
    263
    264                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのラベルを付けません。
    265                        if( isKeyLbl[i] ) {
    266                                headerCellValue( "",nColIndex++ );
    267                        }
    268                }
    269
    270                // 余計なセルを削除
    271                excel.removeCell( nColIndex );
    272        }
    273
    274        /**
    275         * PrintWriter に DBTableModelの項目名情報を書き込みます。
    276         * 第一カラム目は、項目名情報を示す "#Name" を書き込みます。
    277         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    278         *
    279         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
    280         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    281         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    282         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    283         *
    284         * @param       table  DBTableModelオブジェクト
    285         * @param       writer PrintWriterオブジェクト
    286         */
    287        @Override
    288        protected void writeName( final DBTableModel table,final PrintWriter writer ) {
    289                short nColIndex = 0;
    290                excel.createRow( nRowIndex++ );
    291                if( useNumber ) { headerCellValue( "Name",nColIndex++ ); }
    292
    293                for( int i=0; i<numberOfColumns; i++ ) {
    294                        final int clm = clmNo[i];
    295                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    296                                nColIndex++;
    297                                continue;
    298                        }
    299
    300                        final String name = table.getColumnName( clm );
    301                        headerCellValue( name,nColIndex++ );
    302
    303                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムの項目名を付けません。
    304                        if( isKeyLbl[i] ) {
    305                                headerCellValue( "",nColIndex++ );
    306                        }
    307                }
    308
    309                // 余計なセルを削除
    310                excel.removeCell( nColIndex );
    311        }
    312
    313        /**
    314         * PrintWriter に DBTableModelのサイズ情報を書き込みます。
    315         * 第一カラム目は、サイズ情報を示す "#Size" を書き込みます。
    316         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    317         *
    318         * @og.rev 3.5.5.5 (2004/04/23) DBColumn の size と maxlength の 意味を変更
    319         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    320         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    321         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    322         *
    323         * @param       table  DBTableModelオブジェクト
    324         * @param       writer PrintWriterオブジェクト
    325         */
    326        @Override
    327        protected void writeSize( final DBTableModel table,final PrintWriter writer ) {
    328                short nColIndex = 0;
    329                excel.createRow( nRowIndex++ );
    330                if( useNumber ) { headerCellValue( "Size",nColIndex++ ); }
    331
    332                for( int i=0; i<numberOfColumns; i++ ) {
    333                        final int clm = clmNo[i];
    334                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    335                                nColIndex++;
    336                                continue;
    337                        }
    338
    339                        // 4.0.0 (2005/01/31) メソッド名変更
    340                        final String val = String.valueOf( dbColumn[clm].getTotalSize() );
    341                        headerCellValue( val,nColIndex++ );
    342
    343                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのサイズを付けません。
    344                        if( isKeyLbl[i] ) {
    345                                headerCellValue( "",nColIndex++ );
    346                        }
    347                }
    348
    349                // 余計なセルを削除
    350                excel.removeCell( nColIndex );
    351        }
    352
    353        /**
    354         * PrintWriter に DBTableModelのクラス名情報を書き込みます。
    355         * 第一カラム目は、サイズ情報を示す "#Class" を書き込みます。
    356         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    357         *
    358         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    359         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    360         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    361         *
    362         * @param       table  DBTableModelオブジェクト
    363         * @param       writer PrintWriterオブジェクト
    364         */
    365        @Override
    366        protected void writeClass( final DBTableModel table,final PrintWriter writer ) {
    367                short nColIndex = 0;
    368                excel.createRow( nRowIndex++ );
    369                if( useNumber ) { headerCellValue( "Class",nColIndex++ ); }
    370
    371                for( int i=0; i<numberOfColumns; i++ ) {
    372                        final int clm = clmNo[i];
    373                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    374                                nColIndex++;
    375                                continue;
    376                        }
    377
    378                        final String val = dbColumn[clm].getClassName();
    379                        headerCellValue( val,nColIndex++ );
    380
    381                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのクラス名を付けません。
    382                        if( isKeyLbl[i] ) {
    383                                headerCellValue( "",nColIndex++ );
    384                        }
    385                }
    386
    387                // 余計なセルを削除
    388                excel.removeCell( nColIndex );
    389        }
    390
    391        /**
    392         * PrintWriter に セパレーターを書き込みます。
    393         * 第一カラム目は、サイズ情報を示す "#----" を書き込みます。
    394         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    395         *
    396         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    397         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    398         *
    399         * @param       table  DBTableModelオブジェクト
    400         * @param       writer PrintWriterオブジェクト
    401         */
    402        @Override
    403        protected void writeSeparator( final DBTableModel table,final PrintWriter writer ) {
    404                final String SEP = "----" ;
    405
    406                short nColIndex = 0;
    407                excel.createRow( nRowIndex++ );
    408                if( useNumber ) { headerCellValue( SEP,nColIndex++ ); }
    409
    410                for( int i=0; i<numberOfColumns; i++ ) {
    411                        final int clm = clmNo[i];
    412                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    413                                nColIndex++;
    414                                continue;
    415                        }
    416                        headerCellValue( SEP,nColIndex++ );
    417
    418                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのセパレーターを書き込みます。
    419                        if( isKeyLbl[i] ) {
    420                                headerCellValue( SEP,nColIndex++ );
    421                        }
    422                }
    423
    424                // 余計なセルを削除
    425                excel.removeCell( nColIndex );
    426        }
    427
    428        /**
    429         * ExcelModel#setCellValue(String.int) の第一カラムの場合に、コメントの # を追加する簡易メソッドです。
    430         *
    431         * colNo == 0 の場合だけ、引数の val に、"#" を追加します。
    432         * これは、openGion特有のコメント行を意味するため、ヘッダーレコードにのみ適用します。
    433         *
    434         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    435         *
    436         * @param       val   設定文字列
    437         * @param       colNo 行番号
    438         */
    439        private void headerCellValue( final String val,final int colNo ) {
    440                if( colNo == 0 ) {
    441                        excel.setCellValue( "#" + val,colNo );
    442                }
    443                else {
    444                        excel.setCellValue( val,colNo );
    445                }
    446        }
    447
    448        /**
    449         * writeData( DBTableModel ,PrintWriter ) の呼び出し前に、一度だけ実行すればよい処理をまとめました。
    450         *
    451         * 通常のTableWriterは、１回の DBTableModel の読み込みで、１ファイルだけ出力します。
    452         * そのため、writeData( DBTableModel ,PrintWriter ) の呼び出し処理の初めに、初期化している処理があります。
    453         * EXCELでは、Sheet 処理と、File 処理のループがあり、本来１回だけ初期化すればよいのですが、
    454         * writeData( DBTableModel ,PrintWriter ) が複数回呼ばれるため、無駄な処理になってしまいます。
    455         * ただし、今までは、ローカル変数でしたが、インスタンス変数に変更しています。
    456         *
    457         * @og.rev 6.0.2.0 (2014/09/19) 新規追加
    458         */
    459        private void initWriteData() {
    460                final DBTableModel table = getDBTableModel();
    461                pageBreakClmNo  = table.getColumnNo( pageBreakColumn,false );   // シートブレイクするキーとなるカラムNo
    462                fileBreakClmNo  = table.getColumnNo( fileBreakColumn,false );   // ファイルブレイクするキーとなるカラムNo
    463
    464                hLinkKeyClmNo   = table.getColumnNo( hLinkKeyColumn,false );    // Hyperlinkを作成するキーとなるカラムNo
    465                hLinkValClmNo   = table.getColumnNo( hLinkValColumn,false );    // Hyperlinkを作成する値となるカラムNo
    466
    467                nvar    = new boolean[numberOfColumns];
    468                isNums  = new boolean[numberOfColumns];                         // 6.0.2.0 (2014/09/19) NUMBER型かどうか
    469                final boolean useRenderer = isUseRenderer();            // 5.2.1.0 (2010/10/01)
    470
    471                for( int i=0; i<numberOfColumns; i++ ) {
    472                        final int clm = clmNo[i];
    473                        if( clm < 0 ) { continue; }                     // 6.0.1.2 (2014/08/08) カラム飛ばし
    474
    475                        // 5.7.6.3 (2014/05/23) ここでレンデラ時の文字タイプ判定を行う
    476                        if( useRenderer && dbColumn[clm].isStringOutput() ){
    477                                isNums[i]   = false;
    478                        }
    479                        else {
    480                                final NativeType nativeType = dbColumn[clm].getNativeType();
    481                                switch( nativeType ) {
    482                                        case INT    :
    483                                        case LONG   :
    484                                        case DOUBLE :
    485                                                isNums[i]   = true;
    486                                                        break;
    487                                        case STRING :
    488                                        case CALENDAR :
    489                                        default :
    490                                                        isNums[i]   = false;
    491                                                        break;
    492                                }
    493                        }
    494                        nvar[i] = "NVAR".equals( dbColumn[clm].getDbType()) ;
    495                }
    496        }
    497
    498        /**
    499         * PrintWriter に DBTableModelのテーブル情報を書き込みます。
    500         * このクラスでは，データを ダブルコーテーション(")で囲みます。
    501         * PrintWriter に DBTableModelのテーブル情報を書き込みます。
    502         *
    503         * @og.rev 3.8.0.1 (2005/06/17) DBTypeが NVAR の場合は、元のUnicodeに戻します。
    504         * @og.rev 3.8.5.3 (2006/08/07) DBType の nativeType に対応した、CELL_TYPE をセットします。
    505         * @og.rev 4.1.1.2 (2008/02/28) NativeタイプをEnum型(fukurou.model.NativeType)に変更
    506         * @og.rev 5.1.4.0 (2010/03/01) columns 対応
    507         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    508         * @og.rev 5.2.1.0 (2010/10/01) useRenderer 対応
    509         * @og.rev 5.7.6.3 (2014/05/23) stringOutput対応
    510         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    511         * @og.rev 6.0.4.0 (2014/11/28) データ出力用のレンデラー
    512         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    513         *
    514         * @param       table  DBTableModelオブジェクト
    515         * @param       writer PrintWriterオブジェクト(未使用)
    516         */
    517        @Override
    518        protected void writeData( final DBTableModel table,final PrintWriter writer ) {
    519                final int numberOfRows    = table.getRowCount();
    520                final boolean useRenderer = isUseRenderer();                            // 5.2.1.0 (2010/10/01)
    521
    522                if( useAutoCellSize ) {
    523                        excel.setDataStartRow( nRowIndex );                             // データ行の書き込み開始位置の行番号を設定
    524                }
    525
    526                int rowNo = 1;
    527                for( ; tblRow<numberOfRows; tblRow++ ) {                        // tblRow は、シートブレイクしても連続してカウントアップ
    528                        // まずはファイルブレイク判定
    529                        if( fileBreakClmNo >= 0 ) {
    530                                // ファイルブレイクすると、noPageBreak=false と、noFileBreak=false がセットされる。
    531                                final String nowFBC = table.getValue( tblRow,fileBreakClmNo );
    532                                if( !fileKey.equals( nowFBC ) ) {
    533                                        fileKey = nowFBC;
    534                                        noPageBreak = false;
    535                                        return;
    536                                }
    537                        }
    538                        if( pageBreakClmNo >= 0 ) {
    539                                // シートブレイクすると、新しい sheetName がセットされ、noPageBreak=false がセットされる。
    540                                final String nowPBC = table.getValue( tblRow,pageBreakClmNo );
    541                                if( !sheetName.equals( nowPBC ) ) {
    542                                        sheetName = nowPBC;
    543                                        return;
    544                                }
    545                        }
    546
    547                        short nColIndex = 0;
    548                        excel.createRow( nRowIndex++ );
    549                        if( useNumber ) { excel.setCellValue( String.valueOf( rowNo++ ),nColIndex++,true ); }
    550
    551                        for( int i=0; i<numberOfColumns; i++ ) {
    552                                final int clm = clmNo[i];
    553                                if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    554                                        nColIndex++;
    555                                        continue;
    556                                }
    557
    558                                String val = table.getValue( tblRow,clm );
    559
    560                                // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのラベルを付けません。
    561                                if( isKeyLbl[i] ) {
    562                                        excel.setCellValue( val,nColIndex++,isNums[i] );
    563                                }
    564
    565                                if( nvar[i] ) {
    566                                        val = StringUtil.getReplaceEscape( val );
    567                                }
    568                                // 5.2.1.0 (2010/10/01) useRenderer 対応
    569                                else if( useRenderer ) {
    570                                        // 6.0.4.0 (2014/11/28) データ出力用のレンデラー
    571                                        val = dbColumn[clm].getWriteValue( val );
    572                                }
    573
    574                                excel.setCellValue( val,nColIndex,isNums[i] );
    575
    576                                // ハイパーリンク設定は、文字の設定の後で行います。
    577                                if( clm == hLinkKeyClmNo && hLinkValClmNo >= 0 ) {
    578                                        final String shtNm = table.getValue( tblRow,hLinkValClmNo );
    579                                        excel.setCellLink( shtNm,nColIndex );
    580                                }
    581
    582                                nColIndex++;
    583                        }
    584
    585                        // 余計なセルを削除
    586                        excel.removeCell( nColIndex );
    587                }
    588                noPageBreak = false;    // 最後まで終了した
    589                fileKey         = null;         // 最後まで終了した
    590        }
    591
    592        /**
    593         * 出力先ディレクトリとファイル名をセットします。
    594         * これは、EXCEL追加機能として実装されています。
    595         *
    596         * 雛形ファイルとの関係から、内部的に filename は合成しておきます。
    597         *
    598         * @og.rev 3.5.4.3 (2004/01/05) 新規作成
    599         * @og.rev 6.0.2.0 (2014/09/19) ディレクトリとファイルを分けて管理します。
    600         *
    601         * @param   dir   出力先ディレクトリ
    602         * @param   fname 出力ファイル名
    603         */
    604        @Override
    605        public void setFilename( final String dir , final String fname ) {
    606                filename        = StringUtil.urlAppend( dir,fname ) ;
    607
    608                directory       = dir;
    609                fileKey         = fname;
    610        }
    611
    612        /**
    613         * DBTableModelのデータとして読み込むときのシート名を設定します。
    614         * 初期値は、"Sheet1" です。
    615         * これは、EXCEL追加機能として実装されています。
    616         *
    617         * @og.rev 3.5.4.2 (2003/12/15) 新規追加
    618         *
    619         * @param   shtName シート名
    620         */
    621        @Override
    622        public void setSheetName( final String shtName ) {
    623                if( shtName != null ) { this.sheetName = shtName; }
    624        }
    625
    626        /**
    627         * このクラスが、EXCEL対応機能を持っているかどうかを返します。
    628         *
    629         * EXCEL対応機能とは、シート名のセット、雛型参照ファイル名のセット、
    630         * 書き込み元ファイルのFileオブジェクト取得などの、特殊機能です。
    631         * 本来は、インターフェースを分けるべきと考えますが、taglib クラス等の
    632         * 関係があり、問い合わせによる条件分岐で対応します。
    633         *
    634         * @og.rev 3.5.4.3 (2004/01/05) 新規追加
    635         *
    636         * @return      EXCEL対応機能を持っているかどうか(常に true)
    637         */
    638        @Override
    639        public boolean isExcel() {
    640                return true;
    641        }
    642
    643        /**
    644         * EXCEL雛型参考ファイル名をセットします。(DIR + Filename)
    645         * これは、EXCEL追加機能として実装されています。
    646         *
    647         * @og.rev 3.5.4.3 (2004/01/05) 新規作成
    648         *
    649         * @param   rfname EXCEL雛型参考ファイル名
    650         */
    651        @Override
    652        public void setRefFilename( final String rfname ) {
    653                refFilename = rfname;
    654        }
    655
    656        /**
    657         * 雛形のシート名を、そのまま使用する(true)か、新規、または、外部指定のシート名を使用する(false)を指定します。(初期値:false[外部指定のシート名を使用])。
    658         *
    659         * ※ Ver5では、追記モード時に、指定シートが存在した場合は上書きします(初期値:false[上書きしない])。5.9.12.1 (2016/09/09)
    660         *    Ver6では、追記モード時に、雛形を指定できないため、雛形のシート名を、そのまま使用する(true)か、
    661         *    新規、または、外部指定のシート名を使用する(false)を指定する属性になります。
    662         *
    663         * @og.rev 6.5.0.0 (2016/09/30) sheetOverwrite で、雛形シートの使用時に、元のシート名を使用します。
    664         *
    665         * @param   flag 元のシート名を使用するかどうか[true:使用する/false:新規、または、外部指定のシート名を使用]
    666         */
    667        @Override
    668        public void setSheetOverwrite( final boolean flag ) {
    669                sheetOverwrite = flag;
    670        }
    671
    672        /**
    673         * EXCELで、出力処理の最後にセルの計算式の再計算をさせるシート名をCSV形式で指定します。
    674         *
    675         * @og.rev 6.5.0.0 (2016/09/30) recalcSheetName で、セル内の計算式を再計算させるシート名を指定。5.9.12.1 (2016/09/09)
    676         *
    677         * @param  sheet 対象シート名をCSV形式で指定
    678         */
    679        @Override
    680        public void setRecalcSheetName( final String sheet ){
    681                recalcSheetNames = StringUtil.csv2Array( sheet);
    682        }
    683
    684        /**
    685         * EXCEL雛型参考ファイルのシート名を設定します。
    686         * これは、EXCEL追加機能として実装されています。
    687         *
    688         * EXCELファイルを書き出す時に、雛型として参照するシート名を指定します。
    689         * これにより、複数の形式の異なるデータを順次書き出したり(appendモードを併用)する
    690         * ことや、シートを指定して新規にEXCELを作成する場合にフォームを設定する事が可能になります。
    691         * 初期値は、null(第一シート) です。
    692         *
    693         * @og.rev 3.5.4.3 (2004/01/05) 新規追加
    694         *
    695         * @param   rshtName シート名
    696         */
    697        @Override
    698        public void setRefSheetName( final String rshtName )  {
    699                if( rshtName != null ) { refSheetName = rshtName; }
    700        }
    701
    702        /**
    703         * データの書き込み開始位置を設定します(初期値:0)。
    704         *
    705         * TAB区切りテキストやEXCEL等のデータの書き込みの開始位置を指定します。
    706         * 属性名は、行を飛ばす処理ということで、readTable タグと同じ名称です。
    707         * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
    708         * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
    709         * 行の読み飛ばしと、カラムの読み飛ばし(columns)、refFileURL、refFilename、
    710         * refSheetName とともに使用すれば、ある程度のレイアウト設定が可能です。
    711         * なお、この機能は、TableWriter_Excel のみに実装します。
    712         *
    713         * @og.rev 6.0.1.2 (2014/08/08) 新規作成
    714         *
    715         * @param       skipCount 書き込み開始位置
    716         */
    717        @Override
    718        public void setSkipRowCount( final int skipCount ) {
    719                skipRowCount = skipCount ;
    720        }
    721
    722        /**
    723         * EXCEL出力時のデフォルトフォント名を設定します。
    724         * これは、EXCEL追加機能として実装されています。
    725         *
    726         * EXCELファイルを書き出す時に、デフォルトフォント名を指定します。
    727         * フォント名は、EXCELのフォント名をそのまま使用してください。
    728         * 内部的に、POI の org.apache.poi.hssf.usermodel.HSSFFont#setFontName( String )
    729         * に設定されます。
    730         * 雛形ファイルを使用しない場合のみ、有効です。
    731         *
    732         * @og.rev 3.8.5.3 (2006/08/07) 新規追加
    733         *
    734         * @param   font デフォルトフォント名
    735         */
    736        @Override
    737        public void setFontName( final String font ) {
    738                fontName = font ;
    739        }
    740
    741        /**
    742         * EXCEL出力時のデフォルトフォントポイント数を設定します。
    743         * これは、EXCEL追加機能として実装されています。
    744         *
    745         * EXCELファイルを書き出す時に、デフォルトポイント数を指定します。
    746         * 内部的に、POI の org.apache.poi.hssf.usermodel.HSSFFont#setFontHeightInPoints( short )
    747         * に設定されます。
    748         * 雛形ファイルを使用しない場合のみ、有効です。
    749         *
    750         * @og.rev 3.8.5.3 (2006/08/07) 新規追加
    751         *
    752         * @param   point デフォルトフォントポイント数
    753         */
    754        @Override
    755        public void setFontPoint( final short point ) {
    756                fontPoint = point;
    757        }
    758
    759        /**
    760         * EXCEL出力時に、データを書き込んだ範囲に罫線を入れるかどうかを指定します。
    761         *
    762         * データを書き込んでEXCELを作成しても、ノーマルのセルに値がセットされている
    763         * だけなので、ある程度加工が必要です。
    764         * そこで、データのセットされたセルに罫線を入れることで、それなりのデータが
    765         * 出力された感じになります。
    766         * この設定と、useAutoCellSize="true" で、セルの幅を自動調整すれば、見栄えが良くなります。
    767         * なお、この機能は、TableWriter_Excel のみに実装します。
    768         *
    769         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    770         *
    771         * @param       useCellStyle 罫線を入れるかどうか(true:入れる/false:入れない)
    772         * @see         #setUseAutoCellSize( boolean )
    773         */
    774        @Override
    775        public void setUseCellStyle( final boolean useCellStyle ) {
    776                this.useCellStyle = useCellStyle;
    777        }
    778
    779        /**
    780         * EXCEL出力時に、セルの幅をデータの幅に自動的に合わせるかどうかを指定します。
    781         *
    782         * データを書き込んでEXCELを作成しても、ノーマルのセルに値がセットされている
    783         * だけなので、ある程度加工が必要です。
    784         * そこで、データのセットされたセルの幅を自動調整することで、それなりのデータが
    785         * 出力された感じになります。
    786         * この設定と、useCellStyle="true" で、セルの罫線を自動設定すれば、見栄えが良くなります。
    787         * なお、この機能は、TableWriter_Excel のみに実装します。
    788         *
    789         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    790         *
    791         * @param       useAutoCellSize データの幅に自動的に合わせるかどうか(true:自動調整/false:何もしない)
    792         * @see         #setUseCellStyle( boolean )
    793         */
    794        @Override
    795        public void setUseAutoCellSize( final boolean useAutoCellSize ) {
    796                this.useAutoCellSize = useAutoCellSize;
    797        }
    798
    799        /**
    800         * EXCEL出力時に、セルの有効範囲を設定するかどうかを指定します。
    801         *
    802         * セルの有効範囲というのは、EXCELでの 空行、空列の存在しない範囲を指します。
    803         * 通常、空行でも、データとして残っている場合は、EXCELのセルオブジェクトは存在します。
    804         * ここで、useActiveWorkbook="true" とすると、空行、空列を削除します。
    805         *
    806         * 雛形を使用した場合は、データより多めに設定した計算などは、この処理で
    807         * 削除されますので、データサイズにフィットさせることができます。
    808         * なお、この機能は、TableWriter_Excel のみに実装します。
    809         *
    810         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    811         *
    812         * @param       useActiveWorkbook セルの有効範囲を設定するかどうか(true:設定する/false:そのまま)
    813         */
    814        @Override
    815        public void setUseActiveWorkbook( final boolean useActiveWorkbook ) {
    816                this.useActiveWorkbook = useActiveWorkbook;
    817        }
    818
    819        /**
    820         * EXCEL出力時に、シートブレイクするキーとなるカラム名を指定します(このカラムの値がシート名になります)。
    821         *
    822         * EXCEL帳票では、帳票雛形に、PAGE_BRAKE キーを設定しましたが、TableWriterでは、
    823         * メモリ上のカラムの値が変更したときに、シートブレイクさせることができます。
    824         * このカラムの値がキーブレイクすると、新しいシートに書き出し始めます。
    825         * シート名は、このカラムの値（キーブレイクする値）です。
    826         *
    827         * 雛形ファイルを使用する場合、雛形シートもキーブレイクに伴って、＋１されます。
    828         * つまり、雛形シートとデータシートは同時に変更されます。
    829         * ただし、雛形シートは、最後の雛形シートで止まります。
    830         * これは、雛形シートにヘッダー雛形とボディ雛形を用意しておき、最初のキーブレイクで
    831         * ヘッダーからボディの書き込みにチェンジするイメージで使用できます。
    832         * なお、この機能は、TableWriter_Excel のみに実装します。
    833         *
    834         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    835         *
    836         * @param       pageBreakColumn シートブレイクするキーとなるカラム名を指定
    837         * @see         #setFileBreakColumn( String )
    838         */
    839        @Override
    840        public void setPageBreakColumn( final String pageBreakColumn ) {
    841                this.pageBreakColumn = pageBreakColumn;
    842        }
    843
    844        /**
    845         * EXCEL出力時に、ファイルブレイクするキーとなるカラム名を指定します(このカラムの値がファイル名になります)。
    846         *
    847         * EXCEL帳票では、メモリ上のカラムの値が変更したときに、ファイルブレイクすることができます。
    848         * このカラムの値がキーブレイクすると、新しいファイルに書き出し始めます。
    849         * ファイル名は、このカラムの値（キーブレイクする値）＋ 元の出力ファイル名の拡張子(.xlsなど)です。
    850         * この設定を使用する場合は、出力ファイル名は無視されますが、拡張子だけは使用されます。
    851         *
    852         * 雛形ファイルを使用する場合、雛形ファイルもキーブレイクに伴って、再利用されます。
    853         * 例えば、pageBreakColumn と併用する場合、キーブレイクで雛形シートも最初から適用になります。
    854         * なお、この機能は、TableWriter_Excel のみに実装します。
    855         *
    856         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    857         *
    858         * @param       fileBreakColumn ファイルブレイクするキーとなるカラム名を指定
    859         * @see         #setPageBreakColumn( String )
    860         */
    861        @Override
    862        public void setFileBreakColumn( final String fileBreakColumn ) {
    863                this.fileBreakColumn = fileBreakColumn;
    864        }
    865
    866        /**
    867         * EXCEL出力時に、Hyperlinkを作成するキーとなるカラム名と値となるカラム名を指定します。
    868         *
    869         * ここで、作成するハイパーリンクは、EXCELのシートに対するハイパーリンクです。
    870         * それ以外のリンク(本当のURLやファイル等)のリンクは(今は)作成できません。
    871         * ハイパーリンクを作成するには、①作成するカラム と ②作成する値 が必要です。
    872         * このメソッドで設定するのは、「①:②」という形式でカラム名を指定します。
    873         * ②がなければ、①と同じとします。
    874         * ②の値のシートの存在有無は、無視します。ハイパーリンクを作成するシートを作成する前に
    875         * ハイパーリンクを作成するケースが存在します。
    876         * (例えば、各シートへのリンクを持った一覧を作成してから、明細の各シートを作成する様なケース)
    877         * なお、この機能は、TableWriter_Excel のみに実装します。
    878         *
    879         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    880         *
    881         * @param       hyperLinkColumn Hyperlinkを作成するキーとなるカラム名と値となるカラム名を指定
    882         */
    883        @Override
    884        public void setHyperLinkColumn( final String hyperLinkColumn ) {
    885                if( hyperLinkColumn != null && hyperLinkColumn.length() > 0 ) {
    886                        final int sep = hyperLinkColumn.indexOf( ':' );                                 // ①:② の形式かどうかの確認
    887                        if( sep >= 0 ) {
    888                                hLinkKeyColumn = hyperLinkColumn.substring( 0,sep );    // ①キーカラム
    889                                hLinkValColumn = hyperLinkColumn.substring( sep+1 );    // ②値カラム
    890                        }
    891                        else {
    892                                hLinkKeyColumn = hyperLinkColumn;                                               // ①キーカラム = ②値カラム
    893                                hLinkValColumn = hyperLinkColumn;
    894                        }
    895                }
    896        }
    897
    898        /**
    899         * EXCEL出力時に、Sheet一覧を先頭Sheetに作成する場合のSheet名を指定します。
    900         *
    901         * これは、Workbook に含まれる Sheet 一覧を作成する場合に、利用可能です。
    902         * なお、この機能は、TableWriter_Excel のみに実装します。
    903         *
    904         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    905         *
    906         * @param       sheetName EXCELファイルのシート名
    907         */
    908        @Override
    909        public void setAddTitleSheet( final String sheetName ) {
    910                addTitleSheet = sheetName ;
    911        }
    912}