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.HybsSystemException;
019import org.opengion.fukurou.util.StringUtil;
020import org.opengion.fukurou.system.Closer ;
021import org.opengion.fukurou.system.DateSet;                                                     // 6.4.2.0 (2016/01/29)
022
023import org.apache.poi.poifs.filesystem.POIFSFileSystem;
024import org.apache.poi.util.RecordFormatException;                                       // 6.4.6.0 (2016/05/27) poi-3.15
025
026import org.apache.poi.hssf.usermodel.HSSFWorkbook;
027import org.apache.poi.hssf.usermodel.HSSFSheet;
028import org.apache.poi.hssf.usermodel.HSSFRow;
029import org.apache.poi.hssf.usermodel.HSSFCell;
030import org.apache.poi.hssf.usermodel.HSSFDateUtil;
031import org.apache.poi.hssf.usermodel.HSSFRichTextString;
032
033import java.io.File;
034import java.io.InputStream;
035import java.io.FileInputStream;
036import java.io.IOException;
037
038import java.text.NumberFormat ;
039import java.text.DecimalFormat ;
040import java.util.Iterator ;
041
042/**
043 * 【EXCEL取込】雛形EXCELシートの解析処理を行う為の、HSSFListener 拡張クラスです。
044 * このオブジェクトは、HSSFRequest クラスの addListenerForAllRecords メソッドに渡す
045 * HSSFListener インターフェースを実装しています。また、雛形EXCEL を処理後、ExcelLayout
046 * 管理クラスを取得することが出来ます。
047 *
048 *   ※ 最新のPOIでは、org.apache.poi.ss.usermodel を使う事で、2003形式、2007形式に対応させます。
049 *      report パッケージは保守対象外なので、あえて修正しません。
050 *
051 * @og.rev 3.8.0.0 (2005/06/07) 新規追加
052 * @og.group 帳票システム
053 *
054 * @version  4.0
055 * @author   Kazuhiko Hasegawa
056 * @since    JDK5.0,
057 */
058public class ExcelDataPickup {
059
060        private final ExcelLayout       layout  ;
061
062        private final InputStream       in      ;
063        private final HSSFWorkbook      wb      ;
064        private NumberFormat numFormat  ;
065
066        private final boolean debug             ;
067
068        /**
069         * 雛形EXCELを処理済みのExcelLayoutオブジェクトと、
070         * データEXCELファイル名よりオブジェクトを構築します。
071         *
072         * 内部で、HSSFWorkbook を構築します。
073         *
074         * @param       layout  雛形EXCELを処理済みのExcelLayoutオブジェクト
075         * @param       filename        データEXCELファイル名
076         * @param debug デバッグフラグ
077         */
078        public ExcelDataPickup( final ExcelLayout layout,final File filename, final boolean debug ) {
079                this.layout     = layout;
080                this.debug      = debug;
081
082                try {
083                        in = new FileInputStream( filename );
084
085                        final POIFSFileSystem fs = new POIFSFileSystem( in );
086                        wb = new HSSFWorkbook( fs );
087                }
088                catch( final IOException ex) {
089                        final String errMsg = "ファイル読込みエラー[" + filename.getName() + "]"  ;
090                        throw new HybsSystemException( errMsg,ex );
091                }
092                catch( final RecordFormatException ex) {
093                        final String errMsg = "無効の形式/データが使用されています。[" + filename.getName() + "]"
094                                        + "現バージョンのPOIでは読み取ることが出来ません。"
095                                        + "例えば、自動フィルタの設定されたシートが含まれる場合などです。" ;
096                        throw new HybsSystemException( errMsg,ex );
097                }
098        }
099
100        /**
101         * データEXCELファイル名のシート数を返します。
102         *
103         * 内部で、HSSFWorkbook を構築します。
104         *
105         * @return      シート数
106         */
107        public int getSheetSize() {
108                return wb.getNumberOfSheets();
109        }
110
111        /**
112         * データEXCELファイル名のデータをピックアップします。
113         *
114         * この処理を行うと、ExcelLayout オブジェクトにデータEXCEL解析結果を
115         * 保存しますので、処理側でその結果を取り出して使用します。
116         *
117         * @og.rev 3.8.1.1 (2005/11/21) デバッグ用コメント修正
118         *
119         * @param       modelSheetNo    雛形シート番号
120         * @param       sheetNo データシート番号
121         * @param       loopClm 繰返必須カラム(なければ通常の1対1処理)
122         */
123        public void execute( final int modelSheetNo, final int sheetNo, final String loopClm ) {
124
125                final HSSFSheet sheet = wb.getSheetAt( sheetNo );
126                layout.dataClear();
127
128                if( debug ) { System.out.println( sheetNo + ":" + wb.getSheetName( sheetNo ) ); }
129
130                final Iterator<ExcelLayoutData> ite = layout.getLayoutDataIterator( modelSheetNo,loopClm ) ;
131                while( ite.hasNext() ) {
132                        final ExcelLayoutData data = ite.next();
133                        final String clm  = data.getClm();
134                        final int    edbn = data.getEdbn();
135                        final int    row  = data.getRowNo();
136                        final short  col  = data.getColNo();
137
138        //              if( clm.indexOf( debugClm ) >= 0 ) { debug = true; }
139        //              else { debug = false; }
140
141                        final String val = getValue( sheet,row,col );
142                        layout.addData( clm,edbn,val );
143                        if( debug ) { System.out.println( data.toString() + "=[" + val + "]" ); }
144                }
145        }
146
147        /**
148         * シートオブジェクト(HSSFSheet)から行列番号を指定して値を取り出します。
149         * 行オブジェクト(HSSFRow)や列オブジェクト(HSSFCell)が存在しない場合は、nullを返します。
150         * それ以外は、各セルタイプに応じて、処理を行います。
151         * 関数タイプ(HSSFCell.CELL_TYPE_FORMULA)の場合は、まず、文字列として取り出し、ゼロ文字列の
152         * 場合は、数字タイプとして取り出します。
153         *
154         * @og.rev 3.8.0.9 (2005/10/17) 結果を rtrim(右スペース削除)します。
155         * @og.rev 3.8.1.1 (2005/11/21) デバッグ用コメント修正
156         * @og.rev 3.9.0.5 (2008/11/27) POI3.2対応 引数の型変更(short⇒int)
157         * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX)
158         *
159         * @param       sheet   EXCELのシートオブジェクト
160         * @param       row             行番号
161         * @param       col             列番号
162         *
163         * @return      セルの値
164         */
165        @SuppressWarnings(value={"deprecation"})        // poi-3.15
166        private String getValue( final HSSFSheet sheet,final int row, final int col ) {
167
168                final HSSFRow  oRow  = sheet.getRow(row);
169                if( oRow == null ) { return null; }
170                final HSSFCell oCell = oRow.getCell(col);
171                if( oCell == null ) { return null; }
172
173                String strText = "";
174                HSSFRichTextString richText ;
175        //      final int nCellType = oCell.getCellType();                      // 6.5.0.0 (2016/09/30) poi-3.12
176        //      switch( nCellType ) {                                                           // 6.5.0.0 (2016/09/30) poi-3.12
177                switch( oCell.getCellTypeEnum() ) {                                     // 6.5.0.0 (2016/09/30) poi-3.15
178        //              case HSSFCell.CELL_TYPE_NUMERIC:                                // 6.5.0.0 (2016/09/30) poi-3.12
179                        case NUMERIC:                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
180                                        strText = getNumericTypeString( oCell );
181                                        break;
182        //              case HSSFCell.CELL_TYPE_STRING:                                 // 6.5.0.0 (2016/09/30) poi-3.12
183                        case STRING:                                                                    // 6.5.0.0 (2016/09/30) poi-3.15
184        // POI3.0               strText = oCell.getStringCellValue();
185                                        richText = oCell.getRichStringCellValue();
186                                        strText =  richText.getString();
187                                        if( debug ) { System.out.print( "String :" ); }
188                                        break;
189        //              case HSSFCell.CELL_TYPE_FORMULA:                                // 6.5.0.0 (2016/09/30) poi-3.12
190                        case FORMULA:                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
191        //                              strText = oCell.getCellFormula();
192        // POI3.0               strText = oCell.getStringCellValue();
193                                        richText = oCell.getRichStringCellValue();
194                                        strText =  richText.getString();
195                                        if( strText == null || strText.isEmpty() ) {
196                                                strText = getNumericTypeString( oCell );
197                                        }
198                                        else {
199                                                if( debug ) { System.out.print( "Formula:" ); }
200                                        }
201                                        break;
202        //              case HSSFCell.CELL_TYPE_BOOLEAN:                                // 6.5.0.0 (2016/09/30) poi-3.12
203                        case BOOLEAN:                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
204                                        strText = String.valueOf(oCell.getBooleanCellValue());
205                                        if( debug ) { System.out.print( "Boolean:" ); }
206                                        break;
207        //              case HSSFCell.CELL_TYPE_BLANK :                                 // 6.5.0.0 (2016/09/30) poi-3.12
208                        case BLANK  :                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
209                                        if( debug ) { System.out.print( "Blank  :" ); }
210                                        break;
211        //              case HSSFCell.CELL_TYPE_ERROR :                                 // 6.5.0.0 (2016/09/30) poi-3.12
212                        case ERROR  :                                                                   // 6.5.0.0 (2016/09/30) poi-3.15
213                                        if( debug ) { System.out.print( "Error  :" ); }
214                                        break;
215                        default :
216        //                              if( debug ) { System.out.print( "Other " + nCellType + ":" ); }
217                                        if( debug ) { System.out.print( "Other " + oCell.getCellTypeEnum() + ":" ); }   // 6.5.0.0 (2016/09/30) poi-3.15
218                                break;
219                }
220
221                return StringUtil.rTrim( strText );             // 3.8.0.9 (2005/10/17)
222        }
223
224        /**
225         * セル値が数字の場合に、数字か日付かを判断して、対応する文字列を返します。
226         *
227         * @og.rev 3.8.1.1 (2005/11/21) デバッグ用コメント修正
228         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
229         *   ※ 最新のPOIでは、org.apache.poi.ss.usermodel を使う事で、2003形式、2007形式に対応させます。
230         *      report パッケージは保守対象外なので、あえて修正しません。
231         *
232         * @param oCell Cellオブジェクト
233         *
234         * @return      数字の場合は、文字列に変換した結果を、日付の場合は、"yyyyMMddHHmmss" 形式で返します。
235         */
236        private String getNumericTypeString( final HSSFCell oCell ) {
237                final String strText ;
238
239                final double dval = oCell.getNumericCellValue() ;
240                if( HSSFDateUtil.isCellDateFormatted( oCell ) ) {
241                        strText = DateSet.getDate( HSSFDateUtil.getJavaDate( dval ).getTime() , "yyyyMMddHHmmss" );
242                        if( debug ) { System.out.print( "Date   :" ); }
243                }
244                else {
245                        // 3.8.0.9 (2005/10/17) 数字について、NumberFormat を行います。
246                        if( numFormat == null ) {
247                                numFormat = NumberFormat.getInstance();
248                                if( numFormat instanceof DecimalFormat ) {
249                                        ((DecimalFormat)numFormat).applyPattern( "#.####" );
250                                }
251                        }
252                        strText = numFormat.format( dval );
253                        if( debug ) { System.out.print( "Numeric:" ); }
254                }
255                return strText ;
256        }
257
258        /**
259         * EXCEL をオープンした InputStream を閉じます。
260         */
261        public void close() {
262                Closer.ioClose( in );           // 4.0.0 (2006/01/31) close 処理時の IOException を無視
263        }
264}