    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 タグを目印に、１行(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}