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