    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 = "6.5.0.0 (2016/09/30)" ;
    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         * 第一カラム目は、ラベル情報を示す "#Label" を書き込みます。
    224         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    225         *
    226         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    227         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    228         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    229         * @og.rev 6.2.4.2 (2015/05/29) StringUtil#tagCut(String) をラベルに適用します。
    230         *
    231         * @param       table  DBTableModelオブジェクト
    232         * @param       writer PrintWriterオブジェクト
    233         */
    234        @Override
    235        protected void writeLabel( final DBTableModel table,final PrintWriter writer ) {
    236                short nColIndex = 0;
    237                excel.createRow( nRowIndex++ );
    238                if( useNumber ) { headerCellValue( "Label",nColIndex++ ); }
    239
    240                for( int i=0; i<numberOfColumns; i++ ) {
    241                        final int clm = clmNo[i];
    242                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    243                                nColIndex++;
    244                                continue;
    245                        }
    246
    247                        final String val = StringUtil.tagCut( dbColumn[clm].getLabel() );               // 6.2.4.2 (2015/05/29)
    248                        headerCellValue( val,nColIndex++ );
    249
    250                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのラベルを付けません。
    251                        if( isKeyLbl[i] ) {
    252                                headerCellValue( "",nColIndex++ );
    253                        }
    254                }
    255
    256                // 余計なセルを削除
    257                excel.removeCell( nColIndex );
    258        }
    259
    260        /**
    261         * PrintWriter に DBTableModelの項目名情報を書き込みます。
    262         * 第一カラム目は、項目名情報を示す "#Name" を書き込みます。
    263         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    264         *
    265         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
    266         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    267         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    268         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    269         *
    270         * @param       table  DBTableModelオブジェクト
    271         * @param       writer PrintWriterオブジェクト
    272         */
    273        @Override
    274        protected void writeName( final DBTableModel table,final PrintWriter writer ) {
    275                short nColIndex = 0;
    276                excel.createRow( nRowIndex++ );
    277                if( useNumber ) { headerCellValue( "Name",nColIndex++ ); }
    278
    279                for( int i=0; i<numberOfColumns; i++ ) {
    280                        final int clm = clmNo[i];
    281                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    282                                nColIndex++;
    283                                continue;
    284                        }
    285
    286                        final String name = table.getColumnName( clm );
    287                        headerCellValue( name,nColIndex++ );
    288
    289                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムの項目名を付けません。
    290                        if( isKeyLbl[i] ) {
    291                                headerCellValue( "",nColIndex++ );
    292                        }
    293                }
    294
    295                // 余計なセルを削除
    296                excel.removeCell( nColIndex );
    297        }
    298
    299        /**
    300         * PrintWriter に DBTableModelのサイズ情報を書き込みます。
    301         * 第一カラム目は、サイズ情報を示す "#Size" を書き込みます。
    302         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    303         *
    304         * @og.rev 3.5.5.5 (2004/04/23) DBColumn の size と maxlength の 意味を変更
    305         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    306         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    307         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    308         *
    309         * @param       table  DBTableModelオブジェクト
    310         * @param       writer PrintWriterオブジェクト
    311         */
    312        @Override
    313        protected void writeSize( final DBTableModel table,final PrintWriter writer ) {
    314                short nColIndex = 0;
    315                excel.createRow( nRowIndex++ );
    316                if( useNumber ) { headerCellValue( "Size",nColIndex++ ); }
    317
    318                for( int i=0; i<numberOfColumns; i++ ) {
    319                        final int clm = clmNo[i];
    320                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    321                                nColIndex++;
    322                                continue;
    323                        }
    324
    325                        // 4.0.0 (2005/01/31) メソッド名変更
    326                        final String val = String.valueOf( dbColumn[clm].getTotalSize() );
    327                        headerCellValue( val,nColIndex++ );
    328
    329                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのサイズを付けません。
    330                        if( isKeyLbl[i] ) {
    331                                headerCellValue( "",nColIndex++ );
    332                        }
    333                }
    334
    335                // 余計なセルを削除
    336                excel.removeCell( nColIndex );
    337        }
    338
    339        /**
    340         * PrintWriter に DBTableModelのクラス名情報を書き込みます。
    341         * 第一カラム目は、サイズ情報を示す "#Class" を書き込みます。
    342         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    343         *
    344         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    345         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    346         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    347         *
    348         * @param       table  DBTableModelオブジェクト
    349         * @param       writer PrintWriterオブジェクト
    350         */
    351        @Override
    352        protected void writeClass( final DBTableModel table,final PrintWriter writer ) {
    353                short nColIndex = 0;
    354                excel.createRow( nRowIndex++ );
    355                if( useNumber ) { headerCellValue( "Class",nColIndex++ ); }
    356
    357                for( int i=0; i<numberOfColumns; i++ ) {
    358                        final int clm = clmNo[i];
    359                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    360                                nColIndex++;
    361                                continue;
    362                        }
    363
    364                        final String val = dbColumn[clm].getClassName();
    365                        headerCellValue( val,nColIndex++ );
    366
    367                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのクラス名を付けません。
    368                        if( isKeyLbl[i] ) {
    369                                headerCellValue( "",nColIndex++ );
    370                        }
    371                }
    372
    373                // 余計なセルを削除
    374                excel.removeCell( nColIndex );
    375        }
    376
    377        /**
    378         * PrintWriter に セパレーターを書き込みます。
    379         * 第一カラム目は、サイズ情報を示す "#----" を書き込みます。
    380         * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
    381         *
    382         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    383         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    384         *
    385         * @param       table  DBTableModelオブジェクト
    386         * @param       writer PrintWriterオブジェクト
    387         */
    388        @Override
    389        protected void writeSeparator( final DBTableModel table,final PrintWriter writer ) {
    390                final String SEP = "----" ;
    391
    392                short nColIndex = 0;
    393                excel.createRow( nRowIndex++ );
    394                if( useNumber ) { headerCellValue( SEP,nColIndex++ ); }
    395
    396                for( int i=0; i<numberOfColumns; i++ ) {
    397                        final int clm = clmNo[i];
    398                        if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    399                                nColIndex++;
    400                                continue;
    401                        }
    402                        headerCellValue( SEP,nColIndex++ );
    403
    404                        // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのセパレーターを書き込みます。
    405                        if( isKeyLbl[i] ) {
    406                                headerCellValue( SEP,nColIndex++ );
    407                        }
    408                }
    409
    410                // 余計なセルを削除
    411                excel.removeCell( nColIndex );
    412        }
    413
    414        /**
    415         * ExcelModel#setCellValue(String.int) の第一カラムの場合に、コメントの # を追加する簡易メソッドです。
    416         *
    417         * colNo == 0 の場合だけ、引数の val に、"#" を追加します。
    418         * これは、openGion特有のコメント行を意味するため、ヘッダーレコードにのみ適用します。
    419         *
    420         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    421         *
    422         * @param       val   設定文字列
    423         * @param       colNo 行番号
    424         */
    425        private void headerCellValue( final String val,final int colNo ) {
    426                if( colNo == 0 ) {
    427                        excel.setCellValue( "#" + val,colNo );
    428                }
    429                else {
    430                        excel.setCellValue( val,colNo );
    431                }
    432        }
    433
    434        /**
    435         * writeData( DBTableModel ,PrintWriter ) の呼び出し前に、一度だけ実行すればよい処理をまとめました。
    436         *
    437         * 通常のTableWriterは、１回の DBTableModel の読み込みで、１ファイルだけ出力します。
    438         * そのため、writeData( DBTableModel ,PrintWriter ) の呼び出し処理の初めに、初期化している処理があります。
    439         * EXCELでは、Sheet 処理と、File 処理のループがあり、本来１回だけ初期化すればよいのですが、
    440         * writeData( DBTableModel ,PrintWriter ) が複数回呼ばれるため、無駄な処理になってしまいます。
    441         * ただし、今までは、ローカル変数でしたが、インスタンス変数に変更しています。
    442         *
    443         * @og.rev 6.0.2.0 (2014/09/19) 新規追加
    444         */
    445        private void initWriteData() {
    446                final DBTableModel table = getDBTableModel();
    447                pageBreakClmNo  = table.getColumnNo( pageBreakColumn,false );   // シートブレイクするキーとなるカラムNo
    448                fileBreakClmNo  = table.getColumnNo( fileBreakColumn,false );   // ファイルブレイクするキーとなるカラムNo
    449
    450                hLinkKeyClmNo   = table.getColumnNo( hLinkKeyColumn,false );    // Hyperlinkを作成するキーとなるカラムNo
    451                hLinkValClmNo   = table.getColumnNo( hLinkValColumn,false );    // Hyperlinkを作成する値となるカラムNo
    452
    453                nvar    = new boolean[numberOfColumns];
    454                isNums  = new boolean[numberOfColumns];                         // 6.0.2.0 (2014/09/19) NUMBER型かどうか
    455                final boolean useRenderer = isUseRenderer();            // 5.2.1.0 (2010/10/01)
    456
    457                for( int i=0; i<numberOfColumns; i++ ) {
    458                        final int clm = clmNo[i];
    459                        if( clm < 0 ) { continue; }                     // 6.0.1.2 (2014/08/08) カラム飛ばし
    460
    461                        // 5.7.6.3 (2014/05/23) ここでレンデラ時の文字タイプ判定を行う
    462                        if( useRenderer && dbColumn[clm].isStringOutput() ){
    463                                isNums[i]   = false;
    464                        }
    465                        else {
    466                                final NativeType nativeType = dbColumn[clm].getNativeType();
    467                                switch( nativeType ) {
    468                                        case INT    :
    469                                        case LONG   :
    470                                        case DOUBLE :
    471                                                isNums[i]   = true;
    472                                                        break;
    473                                        case STRING :
    474                                        case CALENDAR :
    475                                        default :
    476                                                        isNums[i]   = false;
    477                                                        break;
    478                                }
    479                        }
    480                        nvar[i] = "NVAR".equals( dbColumn[clm].getDbType()) ;
    481                }
    482        }
    483
    484        /**
    485         * PrintWriter に DBTableModelのテーブル情報を書き込みます。
    486         * このクラスでは，データを ダブルコーテーション(")で囲みます。
    487         * PrintWriter に DBTableModelのテーブル情報を書き込みます。
    488         *
    489         * @og.rev 3.8.0.1 (2005/06/17) DBTypeが NVAR の場合は、元のUnicodeに戻します。
    490         * @og.rev 3.8.5.3 (2006/08/07) DBType の nativeType に対応した、CELL_TYPE をセットします。
    491         * @og.rev 4.1.1.2 (2008/02/28) NativeタイプをEnum型(fukurou.model.NativeType)に変更
    492         * @og.rev 5.1.4.0 (2010/03/01) columns 対応
    493         * @og.rev 5.1.4.0 (2010/03/01) useNumber属性対応
    494         * @og.rev 5.2.1.0 (2010/10/01) useRenderer 対応
    495         * @og.rev 5.7.6.3 (2014/05/23) stringOutput対応
    496         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    497         * @og.rev 6.0.4.0 (2014/11/28) データ出力用のレンデラー
    498         * @og.rev 6.2.3.0 (2015/05/01) writeKeyLabel 属性を追加
    499         *
    500         * @param       table  DBTableModelオブジェクト
    501         * @param       writer PrintWriterオブジェクト(未使用)
    502         */
    503        @Override
    504        protected void writeData( final DBTableModel table,final PrintWriter writer ) {
    505                final int numberOfRows    = table.getRowCount();
    506                final boolean useRenderer = isUseRenderer();                            // 5.2.1.0 (2010/10/01)
    507
    508                if( useAutoCellSize ) {
    509                        excel.setDataStartRow( nRowIndex );                             // データ行の書き込み開始位置の行番号を設定
    510                }
    511
    512                int rowNo = 1;
    513                for( ; tblRow<numberOfRows; tblRow++ ) {                        // tblRow は、シートブレイクしても連続してカウントアップ
    514                        // まずはファイルブレイク判定
    515                        if( fileBreakClmNo >= 0 ) {
    516                                // ファイルブレイクすると、noPageBreak=false と、noFileBreak=false がセットされる。
    517                                final String nowFBC = table.getValue( tblRow,fileBreakClmNo );
    518                                if( !fileKey.equals( nowFBC ) ) {
    519                                        fileKey = nowFBC;
    520                                        noPageBreak = false;
    521                                        return;
    522                                }
    523                        }
    524                        if( pageBreakClmNo >= 0 ) {
    525                                // シートブレイクすると、新しい sheetName がセットされ、noPageBreak=false がセットされる。
    526                                final String nowPBC = table.getValue( tblRow,pageBreakClmNo );
    527                                if( !sheetName.equals( nowPBC ) ) {
    528                                        sheetName = nowPBC;
    529                                        return;
    530                                }
    531                        }
    532
    533                        short nColIndex = 0;
    534                        excel.createRow( nRowIndex++ );
    535                        if( useNumber ) { excel.setCellValue( String.valueOf( rowNo++ ),nColIndex++,true ); }
    536
    537                        for( int i=0; i<numberOfColumns; i++ ) {
    538                                final int clm = clmNo[i];
    539                                if( clm < 0 ) {                 // 6.0.1.2 (2014/08/08) カラム飛ばし
    540                                        nColIndex++;
    541                                        continue;
    542                                }
    543
    544                                String val = table.getValue( tblRow,clm );
    545
    546                                // 6.2.3.0 (2015/05/01) keyLabelは、後ろのカラムのラベルを付けません。
    547                                if( isKeyLbl[i] ) {
    548                                        excel.setCellValue( val,nColIndex++,isNums[i] );
    549                                }
    550
    551                                if( nvar[i] ) {
    552                                        val = StringUtil.getReplaceEscape( val );
    553                                }
    554                                // 5.2.1.0 (2010/10/01) useRenderer 対応
    555                                else if( useRenderer ) {
    556                                        // 6.0.4.0 (2014/11/28) データ出力用のレンデラー
    557                                        val = dbColumn[clm].getWriteValue( val );
    558                                }
    559
    560                                excel.setCellValue( val,nColIndex,isNums[i] );
    561
    562                                // ハイパーリンク設定は、文字の設定の後で行います。
    563                                if( clm == hLinkKeyClmNo && hLinkValClmNo >= 0 ) {
    564                                        final String shtNm = table.getValue( tblRow,hLinkValClmNo );
    565                                        excel.setCellLink( shtNm,nColIndex );
    566                                }
    567
    568                                nColIndex++;
    569                        }
    570
    571                        // 余計なセルを削除
    572                        excel.removeCell( nColIndex );
    573                }
    574                noPageBreak = false;    // 最後まで終了した
    575                fileKey         = null;         // 最後まで終了した
    576        }
    577
    578        /**
    579         * 出力先ディレクトリとファイル名をセットします。
    580         * これは、EXCEL追加機能として実装されています。
    581         *
    582         * 雛形ファイルとの関係から、内部的に filename は合成しておきます。
    583         *
    584         * @og.rev 3.5.4.3 (2004/01/05) 新規作成
    585         * @og.rev 6.0.2.0 (2014/09/19) ディレクトリとファイルを分けて管理します。
    586         *
    587         * @param   dir   出力先ディレクトリ
    588         * @param   fname 出力ファイル名
    589         */
    590        @Override
    591        public void setFilename( final String dir , final String fname ) {
    592                filename        = StringUtil.urlAppend( dir,fname ) ;
    593
    594                directory       = dir;
    595                fileKey         = fname;
    596        }
    597
    598        /**
    599         * DBTableModelのデータとして読み込むときのシート名を設定します。
    600         * 初期値は、"Sheet1" です。
    601         * これは、EXCEL追加機能として実装されています。
    602         *
    603         * @og.rev 3.5.4.2 (2003/12/15) 新規追加
    604         *
    605         * @param   shtName シート名
    606         */
    607        @Override
    608        public void setSheetName( final String shtName ) {
    609                if( shtName != null ) { this.sheetName = shtName; }
    610        }
    611
    612        /**
    613         * このクラスが、EXCEL対応機能を持っているかどうかを返します。
    614         *
    615         * EXCEL対応機能とは、シート名のセット、雛型参照ファイル名のセット、
    616         * 書き込み元ファイルのFileオブジェクト取得などの、特殊機能です。
    617         * 本来は、インターフェースを分けるべきと考えますが、taglib クラス等の
    618         * 関係があり、問い合わせによる条件分岐で対応します。
    619         *
    620         * @og.rev 3.5.4.3 (2004/01/05) 新規追加
    621         *
    622         * @return      EXCEL対応機能を持っているかどうか(常に true)
    623         */
    624        @Override
    625        public boolean isExcel() {
    626                return true;
    627        }
    628
    629        /**
    630         * EXCEL雛型参考ファイル名をセットします。(DIR + Filename)
    631         * これは、EXCEL追加機能として実装されています。
    632         *
    633         * @og.rev 3.5.4.3 (2004/01/05) 新規作成
    634         *
    635         * @param   rfname EXCEL雛型参考ファイル名
    636         */
    637        @Override
    638        public void setRefFilename( final String rfname ) {
    639                refFilename = rfname;
    640        }
    641
    642        /**
    643         * 雛形のシート名を、そのまま使用する(true)か、新規、または、外部指定のシート名を使用する(false)を指定します。(初期値:false[外部指定のシート名を使用])。
    644         *
    645         * ※ Ver5では、追記モード時に、指定シートが存在した場合は上書きします(初期値:false[上書きしない])。5.9.12.1 (2016/09/09)
    646         *    Ver6では、追記モード時に、雛形を指定できないため、雛形のシート名を、そのまま使用する(true)か、
    647         *    新規、または、外部指定のシート名を使用する(false)を指定する属性になります。
    648         *
    649         * @og.rev 6.5.0.0 (2016/09/30) sheetOverwrite で、雛形シートの使用時に、元のシート名を使用します。
    650         *
    651         * @param   flag 元のシート名を使用するかどうか[true:使用する/false:新規、または、外部指定のシート名を使用]
    652         */
    653        @Override
    654        public void setSheetOverwrite( final boolean flag ) {
    655                sheetOverwrite = flag;
    656        }
    657
    658        /**
    659         * EXCELで、出力処理の最後にセルの計算式の再計算をさせるシート名をカンマ区切りで指定します。
    660         *
    661         * @og.rev 6.5.0.0 (2016/09/30) recalcSheetName で、セル内の計算式を再計算させるシート名を指定。5.9.12.1 (2016/09/09)
    662         *
    663         * @param  sheet 対象シート名をカンマ区切りで指定
    664         */
    665        @Override
    666        public void setRecalcSheetName( final String sheet ){
    667                recalcSheetNames = StringUtil.csv2Array( sheet);
    668        }
    669
    670        /**
    671         * EXCEL雛型参考ファイルのシート名を設定します。
    672         * これは、EXCEL追加機能として実装されています。
    673         *
    674         * EXCELファイルを書き出す時に、雛型として参照するシート名を指定します。
    675         * これにより、複数の形式の異なるデータを順次書き出したり(appendモードを併用)する
    676         * ことや、シートを指定して新規にEXCELを作成する場合にフォームを設定する事が可能になります。
    677         * 初期値は、null(第一シート) です。
    678         *
    679         * @og.rev 3.5.4.3 (2004/01/05) 新規追加
    680         *
    681         * @param   rshtName シート名
    682         */
    683        @Override
    684        public void setRefSheetName( final String rshtName )  {
    685                if( rshtName != null ) { refSheetName = rshtName; }
    686        }
    687
    688        /**
    689         * データの書き込み開始位置を設定します(初期値:0)。
    690         *
    691         * TAB区切りテキストやEXCEL等のデータの書き込みの開始位置を指定します。
    692         * 属性名は、行を飛ばす処理ということで、readTable タグと同じ名称です。
    693         * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
    694         * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
    695         * 行の読み飛ばしと、カラムの読み飛ばし(columns)、refFileURL、refFilename、
    696         * refSheetName とともに使用すれば、ある程度のレイアウト設定が可能です。
    697         * なお、この機能は、TableWriter_Excel のみに実装します。
    698         *
    699         * @og.rev 6.0.1.2 (2014/08/08) 新規作成
    700         *
    701         * @param       skipCount 書き込み開始位置
    702         */
    703        @Override
    704        public void setSkipRowCount( final int skipCount ) {
    705                skipRowCount = skipCount ;
    706        }
    707
    708        /**
    709         * EXCEL出力時のデフォルトフォント名を設定します。
    710         * これは、EXCEL追加機能として実装されています。
    711         *
    712         * EXCELファイルを書き出す時に、デフォルトフォント名を指定します。
    713         * フォント名は、EXCELのフォント名をそのまま使用してください。
    714         * 内部的に、POI の org.apache.poi.hssf.usermodel.HSSFFont#setFontName( String )
    715         * に設定されます。
    716         * 雛形ファイルを使用しない場合のみ、有効です。
    717         *
    718         * @og.rev 3.8.5.3 (2006/08/07) 新規追加
    719         *
    720         * @param   font デフォルトフォント名
    721         */
    722        @Override
    723        public void setFontName( final String font ) {
    724                fontName = font ;
    725        }
    726
    727        /**
    728         * EXCEL出力時のデフォルトフォントポイント数を設定します。
    729         * これは、EXCEL追加機能として実装されています。
    730         *
    731         * EXCELファイルを書き出す時に、デフォルトポイント数を指定します。
    732         * 内部的に、POI の org.apache.poi.hssf.usermodel.HSSFFont#setFontHeightInPoints( short )
    733         * に設定されます。
    734         * 雛形ファイルを使用しない場合のみ、有効です。
    735         *
    736         * @og.rev 3.8.5.3 (2006/08/07) 新規追加
    737         *
    738         * @param   point デフォルトフォントポイント数
    739         */
    740        @Override
    741        public void setFontPoint( final short point ) {
    742                fontPoint = point;
    743        }
    744
    745        /**
    746         * EXCEL出力時に、データを書き込んだ範囲に罫線を入れるかどうかを指定します。
    747         *
    748         * データを書き込んでEXCELを作成しても、ノーマルのセルに値がセットされている
    749         * だけなので、ある程度加工が必要です。
    750         * そこで、データのセットされたセルに罫線を入れることで、それなりのデータが
    751         * 出力された感じになります。
    752         * この設定と、useAutoCellSize="true" で、セルの幅を自動調整すれば、見栄えが良くなります。
    753         * なお、この機能は、TableWriter_Excel のみに実装します。
    754         *
    755         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    756         *
    757         * @param       useCellStyle 罫線を入れるかどうか(true:入れる/false:入れない)
    758         * @see         #setUseAutoCellSize( boolean )
    759         */
    760        @Override
    761        public void setUseCellStyle( final boolean useCellStyle ) {
    762                this.useCellStyle = useCellStyle;
    763        }
    764
    765        /**
    766         * EXCEL出力時に、セルの幅をデータの幅に自動的に合わせるかどうかを指定します。
    767         *
    768         * データを書き込んでEXCELを作成しても、ノーマルのセルに値がセットされている
    769         * だけなので、ある程度加工が必要です。
    770         * そこで、データのセットされたセルの幅を自動調整することで、それなりのデータが
    771         * 出力された感じになります。
    772         * この設定と、useCellStyle="true" で、セルの罫線を自動設定すれば、見栄えが良くなります。
    773         * なお、この機能は、TableWriter_Excel のみに実装します。
    774         *
    775         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    776         *
    777         * @param       useAutoCellSize データの幅に自動的に合わせるかどうか(true:自動調整/false:何もしない)
    778         * @see         #setUseCellStyle( boolean )
    779         */
    780        @Override
    781        public void setUseAutoCellSize( final boolean useAutoCellSize ) {
    782                this.useAutoCellSize = useAutoCellSize;
    783        }
    784
    785        /**
    786         * EXCEL出力時に、セルの有効範囲を設定するかどうかを指定します。
    787         *
    788         * セルの有効範囲というのは、EXCELでの 空行、空列の存在しない範囲を指します。
    789         * 通常、空行でも、データとして残っている場合は、EXCELのセルオブジェクトは存在します。
    790         * ここで、useActiveWorkbook="true" とすると、空行、空列を削除します。
    791         *
    792         * 雛形を使用した場合は、データより多めに設定した計算などは、この処理で
    793         * 削除されますので、データサイズにフィットさせることができます。
    794         * なお、この機能は、TableWriter_Excel のみに実装します。
    795         *
    796         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    797         *
    798         * @param       useActiveWorkbook セルの有効範囲を設定するかどうか(true:設定する/false:そのまま)
    799         */
    800        @Override
    801        public void setUseActiveWorkbook( final boolean useActiveWorkbook ) {
    802                this.useActiveWorkbook = useActiveWorkbook;
    803        }
    804
    805        /**
    806         * EXCEL出力時に、シートブレイクするキーとなるカラム名を指定します(このカラムの値がシート名になります)。
    807         *
    808         * EXCEL帳票では、帳票雛形に、PAGE_BRAKE キーを設定しましたが、TableWriterでは、
    809         * メモリ上のカラムの値が変更したときに、シートブレイクさせることができます。
    810         * このカラムの値がキーブレイクすると、新しいシートに書き出し始めます。
    811         * シート名は、このカラムの値（キーブレイクする値）です。
    812         *
    813         * 雛形ファイルを使用する場合、雛形シートもキーブレイクに伴って、＋１されます。
    814         * つまり、雛形シートとデータシートは同時に変更されます。
    815         * ただし、雛形シートは、最後の雛形シートで止まります。
    816         * これは、雛形シートにヘッダー雛形とボディ雛形を用意しておき、最初のキーブレイクで
    817         * ヘッダーからボディの書き込みにチェンジするイメージで使用できます。
    818         * なお、この機能は、TableWriter_Excel のみに実装します。
    819         *
    820         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    821         *
    822         * @param       pageBreakColumn シートブレイクするキーとなるカラム名を指定
    823         * @see         #setFileBreakColumn( String )
    824         */
    825        @Override
    826        public void setPageBreakColumn( final String pageBreakColumn ) {
    827                this.pageBreakColumn = pageBreakColumn;
    828        }
    829
    830        /**
    831         * EXCEL出力時に、ファイルブレイクするキーとなるカラム名を指定します(このカラムの値がファイル名になります)。
    832         *
    833         * EXCEL帳票では、メモリ上のカラムの値が変更したときに、ファイルブレイクすることができます。
    834         * このカラムの値がキーブレイクすると、新しいファイルに書き出し始めます。
    835         * ファイル名は、このカラムの値（キーブレイクする値）＋ 元の出力ファイル名の拡張子(.xlsなど)です。
    836         * この設定を使用する場合は、出力ファイル名は無視されますが、拡張子だけは使用されます。
    837         *
    838         * 雛形ファイルを使用する場合、雛形ファイルもキーブレイクに伴って、再利用されます。
    839         * 例えば、pageBreakColumn と併用する場合、キーブレイクで雛形シートも最初から適用になります。
    840         * なお、この機能は、TableWriter_Excel のみに実装します。
    841         *
    842         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    843         *
    844         * @param       fileBreakColumn ファイルブレイクするキーとなるカラム名を指定
    845         * @see         #setPageBreakColumn( String )
    846         */
    847        @Override
    848        public void setFileBreakColumn( final String fileBreakColumn ) {
    849                this.fileBreakColumn = fileBreakColumn;
    850        }
    851
    852        /**
    853         * EXCEL出力時に、Hyperlinkを作成するキーとなるカラム名と値となるカラム名を指定します。
    854         *
    855         * ここで、作成するハイパーリンクは、EXCELのシートに対するハイパーリンクです。
    856         * それ以外のリンク(本当のURLやファイル等)のリンクは(今は)作成できません。
    857         * ハイパーリンクを作成するには、①作成するカラム と ②作成する値 が必要です。
    858         * このメソッドで設定するのは、「①:②」という形式でカラム名を指定します。
    859         * ②がなければ、①と同じとします。
    860         * ②の値のシートの存在有無は、無視します。ハイパーリンクを作成するシートを作成する前に
    861         * ハイパーリンクを作成するケースが存在します。
    862         * (例えば、各シートへのリンクを持った一覧を作成してから、明細の各シートを作成する様なケース)
    863         * なお、この機能は、TableWriter_Excel のみに実装します。
    864         *
    865         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    866         *
    867         * @param       hyperLinkColumn Hyperlinkを作成するキーとなるカラム名と値となるカラム名を指定
    868         */
    869        @Override
    870        public void setHyperLinkColumn( final String hyperLinkColumn ) {
    871                if( hyperLinkColumn != null && hyperLinkColumn.length() > 0 ) {
    872                        final int sep = hyperLinkColumn.indexOf( ':' );                                 // ①:② の形式かどうかの確認
    873                        if( sep >= 0 ) {
    874                                hLinkKeyColumn = hyperLinkColumn.substring( 0,sep );    // ①キーカラム
    875                                hLinkValColumn = hyperLinkColumn.substring( sep+1 );    // ②値カラム
    876                        }
    877                        else {
    878                                hLinkKeyColumn = hyperLinkColumn;                                               // ①キーカラム = ②値カラム
    879                                hLinkValColumn = hyperLinkColumn;
    880                        }
    881                }
    882        }
    883
    884        /**
    885         * EXCEL出力時に、Sheet一覧を先頭Sheetに作成する場合のSheet名を指定します。
    886         *
    887         * これは、Workbook に含まれる Sheet 一覧を作成する場合に、利用可能です。
    888         * なお、この機能は、TableWriter_Excel のみに実装します。
    889         *
    890         * @og.rev 6.0.2.0 (2014/09/19) 新規作成
    891         *
    892         * @param       sheetName EXCELファイルのシート名
    893         */
    894        @Override
    895        public void setAddTitleSheet( final String sheetName ) {
    896                addTitleSheet = sheetName ;
    897        }
    898}