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