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.hayabusa.report;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.fukurou.util.Closer ;
021
022import org.apache.poi.poifs.filesystem.POIFSFileSystem;
023import org.apache.poi.hssf.usermodel.HSSFWorkbook;
024import org.apache.poi.hssf.usermodel.HSSFSheet;
025import org.apache.poi.hssf.usermodel.HSSFRow;
026import org.apache.poi.hssf.usermodel.HSSFCell;
027import org.apache.poi.hssf.usermodel.HSSFRichTextString;
028
029import java.io.FileInputStream;
030import java.io.FileOutputStream;
031import java.io.IOException;
032
033import java.util.regex.Pattern;
034import java.util.regex.Matcher;
035
036/**
037 * DBTableReport インターフェース を実装したネイティブEXCEL形式で出力するクラスです。
038 * AbstractDBTableReport を継承していますので,writeReport() のみオーバーライドして,
039 * 固定長文字ファイルの出力機能を実現しています。
040 *
041 * @og.group 帳票システム
042 *
043 * @version  4.0
044 * @author   Kazuhiko Hasegawa
045 * @since    JDK5.0,
046 */
047public class DBTableReport_Excel extends AbstractDBTableReport {
048
049        private static final String EXCEL_FILE_EXT        = ".xls";
050        private static final Pattern PATTERN_KEY =
051                          Pattern.compile("\\{@((\\w+?)(?:_(\\d+?))?)\\}", java.util.regex.Pattern.MULTILINE);
052
053        // POIの解析した式の中に変な属性が付けられて、これを取り除く(patternExcludeInFormula)
054        private static final Pattern PATTERN_EXIN =
055                          Pattern.compile("ATTR\\(semiVolatile\\)", java.util.regex.Pattern.MULTILINE);
056
057        HSSFWorkbook wb = null;
058
059        /**
060         * DBTableModel から データを作成して,PrintWriter に書き出します。
061         *
062         */
063        @Override
064        public void writeReport() {
065                setHeaderFooter();
066                initReader();
067                initWriter();
068                changeSheet();
069                close();
070        }
071
072        /**
073         * POIFSFileSystem を、初期化します。
074         * これは、雛型ファイルの終端まで読取り、処理した場合、もう一度
075         * 初めから読み込みなおす処理を行います。
076         * 基本的に、書き込みも初期化する必要があります。
077         *
078         * メモリ上に読み込んで、繰り返し利用するかどうかは、実装依存です。
079         *
080         */
081        @Override
082        protected void initReader() {
083                if( null != wb ) { wb = null; }
084
085                FileInputStream  istream = null;
086                try {
087                        istream = new FileInputStream(templateFile);
088                        POIFSFileSystem fs = new POIFSFileSystem(istream);
089                        wb = new HSSFWorkbook(fs);
090                }
091                catch( IOException ex ) {
092                        String errMsg = "ファイル名がオープン出来ませんでした。"
093                                                + HybsSystem.CR
094                                                + "  File:" + templateFile;
095                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
096                }
097                finally {
098                        Closer.ioClose( istream );      // 4.0.0 (2006/01/31) close 処理時の IOException を無視
099                }
100        }
101
102        /**
103         * FileOutputStream を、初期化します。
104         * これは、雛型ファイルを終端まで読取り、処理した場合、出力ファイル名を
105         * 変えて、別ファイルとして出力する為のものです。
106         * 基本的に、読取も初期化する必要があります。
107         *
108         * 現在の所、POIはメモリ上にExcelファイルを作成する為、作成したファイルの書く込むを
109         * ファイル閉じる時点に伸ばされます。
110         *
111         */
112        @Override
113        protected void initWriter() {
114                // ここでは処理を行いません。
115        }
116
117        /**
118         * リーダー、ライターの終了処理を行います。
119         * このメソッドが呼ばれたタイミングで、実際にファイル出力を行います。
120         *
121         */
122        protected void close() {
123
124                String filename = htmlDir + HybsSystem.FS + htmlFileKey + EXCEL_FILE_EXT ;
125
126                FileOutputStream fileOut = null;
127                try {
128                        // Write the output to a file
129                        fileOut = new FileOutputStream(filename);
130                        wb.write(fileOut);
131                }
132                catch( IOException ex ) {
133                        wb = null;
134                        String errMsg = "ファイル名がオープン出来ませんでした。"
135                                                + HybsSystem.CR
136                                                + "  File:" + filename;
137                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
138                }
139                finally {
140                        Closer.ioClose( fileOut );      // 4.0.0 (2006/01/31) close 処理時の IOException を無視
141                }
142        }
143
144        /**
145         * Excelの雛型をコピーして、そのシートに帳票データを埋め込みます。
146         * いろいろな属性がある所に、適切に対応していく予定。
147         * 各サブクラスで実装してください。
148         *
149         * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応
150         *
151         */
152        protected void changeSheet() {
153                HSSFSheet patternSheet = wb.getSheetAt(0);
154                while(!rowOver) {
155                        HSSFSheet sheet2 = wb.cloneSheet(0);
156        //              HSSFRow oRow2;
157                        int nFirstRow = sheet2.getFirstRowNum();
158                        int nLastRow  = sheet2.getLastRowNum();
159        //              int nTotalRows = patternSheet.getPhysicalNumberOfRows();
160                        for( int nIndexRow = nFirstRow; nIndexRow <= nLastRow; nIndexRow++) {
161                                HSSFRow oRow = patternSheet.getRow(nIndexRow);
162                                if( null != oRow ) {
163        //                              int nTotalCells = oRow.getPhysicalNumberOfCells();
164                                        // 4.3.4.0 (2008/12/01) POI3.2対応。shortをintにする。
165                                        // short nFirstCell = oRow.getFirstCellNum();
166                                        // short nLastCell  = oRow.getLastCellNum();
167                                        int nFirstCell = oRow.getFirstCellNum();
168                                        int nLastCell  = oRow.getLastCellNum();
169//                                      for( short nIndexCell = nFirstCell; nIndexCell <= nLastCell; nIndexCell++) {
170                                        for( int nIndexCell = nFirstCell; nIndexCell <= nLastCell; nIndexCell++) {
171                                                HSSFCell oCell = oRow.getCell(nIndexCell);
172                                                if( null != oCell ) { changeCell(oCell);}
173                                        }
174                                }
175                        }
176                }
177        }
178
179        /**
180         * セル情報を変更します。
181         *
182         * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応
183         *
184         * @param oCell HSSFCellオブジェクト
185         */
186        protected void changeCell(final HSSFCell oCell) {
187                String strText;
188                HSSFRichTextString richText;
189                int nCellType = oCell.getCellType();
190                switch(nCellType) {
191                        case HSSFCell.CELL_TYPE_FORMULA:
192                                strText = changeData(changeFormulaAttr(oCell.getCellFormula()));
193                                if( null != strText ) {
194        //                              oCell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
195        //                              oCell.setEncoding(HSSFCell.ENCODING_UTF_16);
196                                        oCell.setCellFormula(strText);
197                                }
198                                break;
199                        case HSSFCell.CELL_TYPE_STRING:
200        // POI3.0       strText =  changeData(oCell.getStringCellValue());
201                                richText = oCell.getRichStringCellValue();
202                                strText =  changeData(richText.getString());
203                                if( null != strText ) {
204        //                              oCell.setCellType(HSSFCell.CELL_TYPE_STRING);
205        // POI3.0               oCell.setEncoding(HSSFCell.ENCODING_UTF_16);
206        // POI3.2               oCell.setCellValue( strText );  // POI3.0 Deprecation
207                                        oCell.setCellValue( new HSSFRichTextString(strText) );
208                                }
209                                break;
210        //              case HSSFCell.CELL_TYPE_NUMERIC:
211        //                      break;
212        //              case HSSFCell.CELL_TYPE_BOOLEAN:
213        //                      break;
214        //              case HSSFCell.CELL_TYPE_ERROR:
215        //                      break;
216                        default :
217                                break;
218                }
219        }
220
221        /**
222         * POIで解釈したExcel式の中の変な属性を加工して、出力します。
223         * いろいろな属性がある所に、適切に対応していく予定。
224         * 各サブクラスで実装してください。
225         *
226         * @param       inLine  入力文字列
227         *
228         * @return      出力文字列
229         */
230        protected String changeFormulaAttr( final String inLine ) {
231                // rowOver で、かつ ページブレークかページエンドカットの場合、処理終了。
232                Matcher  matcher = PATTERN_EXIN.matcher(inLine);
233                return (matcher.find()) ? matcher.replaceAll("") : inLine;
234        }
235
236        /**
237         * 入力文字列 を加工して、出力します。
238         * データをテーブルモデルより読み取り、値をセットします。
239         * 各サブクラスで実装してください。
240         *
241         * @param       inLine  入力文字列
242         *
243         * @return      出力文字列. 文字列の変換は要らない場合、nullを返します
244         */
245        @Override
246        protected String changeData( final String inLine ) {
247                boolean bFind = false;
248
249                // rowOver で、かつ ページブレークかページエンドカットの場合、処理終了。
250                Matcher  matcher = PATTERN_KEY.matcher(inLine);
251                StringBuffer sb = new StringBuffer();   // Matcher.appendTail( StringBuffer ) の為
252
253                while (matcher.find()) {
254//                      matcher.appendReplacement(sb, rowOver ? "" : getValue(matcher.group( 1 )));
255                        matcher.appendReplacement(sb, getValue(matcher.group( 1 )));
256                        bFind = true;
257                }
258
259                if( bFind ) {
260                        matcher.appendTail(sb);
261                        return sb.toString();
262                }
263                else {
264                        return null;
265                }
266        }
267
268        /**
269         * 入力文字列 を読み取って、出力します。
270         * tr タグを目印に、1行(trタグ間)ずつ取り出します。
271         * 読み取りを終了する場合は、null を返します。
272         * 各サブクラスで実装してください。
273         * ※ このクラスでは実装されていません。
274         *
275         * @return      出力文字列
276         */
277        @Override
278        protected String readLine() {
279                throw new UnsupportedOperationException();
280        }
281
282        /**
283         * 入力文字列 を読み取って、出力します。
284         * 各サブクラスで実装してください。
285         * ※ このクラスでは実装されていません。
286         *
287         * @param line 入力文字列
288         */
289        @Override
290        protected void println( final String line ) {
291                throw new UnsupportedOperationException();
292        }
293}