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+?))?)\\}", Pattern.MULTILINE);
052
053        // POIの解析した式の中に変な属性が付けられて、これを取り除く(patternExcludeInFormula)
054        private static final Pattern PATTERN_EXIN =
055                          Pattern.compile("ATTR\\(semiVolatile\\)", Pattern.MULTILINE);
056
057        private 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( int nIndexCell = nFirstCell; nIndexCell <= nLastCell; nIndexCell++) {
170                                                HSSFCell oCell = oRow.getCell(nIndexCell);
171                                                if( null != oCell ) { changeCell(oCell);}
172                                        }
173                                }
174                        }
175                }
176        }
177
178        /**
179         * セル情報を変更します。
180         *
181         * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応
182         *
183         * @param oCell HSSFCellオブジェクト
184         */
185        protected void changeCell(final HSSFCell oCell) {
186                String strText;
187                HSSFRichTextString richText;
188                int nCellType = oCell.getCellType();
189                switch(nCellType) {
190                        case HSSFCell.CELL_TYPE_FORMULA:
191                                strText = changeData(changeFormulaAttr(oCell.getCellFormula()));
192                                if( null != strText ) {
193        //                              oCell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
194        //                              oCell.setEncoding(HSSFCell.ENCODING_UTF_16);
195                                        oCell.setCellFormula(strText);
196                                }
197                                break;
198                        case HSSFCell.CELL_TYPE_STRING:
199        // POI3.0       strText =  changeData(oCell.getStringCellValue());
200                                richText = oCell.getRichStringCellValue();
201                                strText =  changeData(richText.getString());
202                                if( null != strText ) {
203        //                              oCell.setCellType(HSSFCell.CELL_TYPE_STRING);
204        // POI3.0               oCell.setEncoding(HSSFCell.ENCODING_UTF_16);
205        // POI3.2               oCell.setCellValue( strText );  // POI3.0 Deprecation
206                                        oCell.setCellValue( new HSSFRichTextString(strText) );
207                                }
208                                break;
209        //              case HSSFCell.CELL_TYPE_NUMERIC:
210        //                      break;
211        //              case HSSFCell.CELL_TYPE_BOOLEAN:
212        //                      break;
213        //              case HSSFCell.CELL_TYPE_ERROR:
214        //                      break;
215                        default :
216                                break;
217                }
218        }
219
220        /**
221         * POIで解釈したExcel式の中の変な属性を加工して、出力します。
222         * いろいろな属性がある所に、適切に対応していく予定。
223         * 各サブクラスで実装してください。
224         *
225         * @param       inLine  入力文字列
226         *
227         * @return      出力文字列
228         */
229        protected String changeFormulaAttr( final String inLine ) {
230                // rowOver で、かつ ページブレークかページエンドカットの場合、処理終了。
231                Matcher  matcher = PATTERN_EXIN.matcher(inLine);
232                return matcher.find() ? matcher.replaceAll("") : inLine;
233        }
234
235        /**
236         * 入力文字列 を加工して、出力します。
237         * データをテーブルモデルより読み取り、値をセットします。
238         * 各サブクラスで実装してください。
239         *
240         * @param       inLine  入力文字列
241         *
242         * @return      出力文字列. 文字列の変換は要らない場合、nullを返します
243         */
244        @Override
245        protected String changeData( final String inLine ) {
246                boolean bFind = false;
247
248                // rowOver で、かつ ページブレークかページエンドカットの場合、処理終了。
249                Matcher  matcher = PATTERN_KEY.matcher(inLine);
250                StringBuffer sb = new StringBuffer();   // Matcher.appendTail( StringBuffer ) の為
251
252                while (matcher.find()) {
253                        matcher.appendReplacement(sb, getValue(matcher.group( 1 )));
254                        bFind = true;
255                }
256
257                if( bFind ) {
258                        matcher.appendTail(sb);
259                        return sb.toString();
260                }
261                else {
262                        return null;
263                }
264        }
265
266        /**
267         * 入力文字列 を読み取って、出力します。
268         * tr タグを目印に、1行(trタグ間)ずつ取り出します。
269         * 読み取りを終了する場合は、null を返します。
270         * 各サブクラスで実装してください。
271         * ※ このクラスでは実装されていません。
272         *
273         * @return      出力文字列
274         */
275        @Override
276        protected String readLine() {
277                throw new UnsupportedOperationException();
278        }
279
280        /**
281         * 入力文字列 を読み取って、出力します。
282         * 各サブクラスで実装してください。
283         * ※ このクラスでは実装されていません。
284         *
285         * @param line 入力文字列
286         */
287        @Override
288        protected void println( final String line ) {
289                throw new UnsupportedOperationException();
290        }
291}