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 ; }           // 親クラスを改造。2回実行されない。
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は、1回の DBTableModel の読み込みで、1ファイルだけ出力します。
438         * そのため、writeData( DBTableModel ,PrintWriter ) の呼び出し処理の初めに、初期化している処理があります。
439         * EXCELでは、Sheet 処理と、File 処理のループがあり、本来1回だけ初期化すればよいのですが、
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で、出力処理の最後にセルの計算式の再計算をさせるシート名をCSV形式で指定します。
660         *
661         * @og.rev 6.5.0.0 (2016/09/30) recalcSheetName で、セル内の計算式を再計算させるシート名を指定。5.9.12.1 (2016/09/09)
662         *
663         * @param  sheet 対象シート名をCSV形式で指定
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         * ファイルの先頭行が、0行としてカウントしますので、設定値は、読み飛ばす
694         * 件数になります。(1と指定すると、1件読み飛ばし、2行目から読み込みます。)
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         * 雛形ファイルを使用する場合、雛形シートもキーブレイクに伴って、+1されます。
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}