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.util.Closer ;
019import org.opengion.fukurou.util.LogWriter;
020
021import org.apache.poi.poifs.filesystem.POIFSFileSystem;
022import org.apache.poi.hssf.eventusermodel.HSSFRequest ;
023import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
024import org.apache.poi.hssf.eventusermodel.HSSFListener ;
025import org.apache.poi.hssf.record.SSTRecord ;
026import org.apache.poi.hssf.record.Record ;
027import org.apache.poi.hssf.record.BOFRecord  ;
028import org.apache.poi.hssf.record.BoundSheetRecord;
029import org.apache.poi.hssf.record.LabelSSTRecord;
030//import org.apache.poi.hssf.record.UnicodeString;
031import org.apache.poi.hssf.record.common.UnicodeString;                 // 5.9.0.0 (2015/09/04) POI変更対応
032
033import java.io.File;
034import java.io.InputStream;
035import java.io.FileInputStream;
036import java.io.IOException;
037
038/**
039 * 【EXCEL取込】雛形EXCELシートの解析処理を行う為の、HSSFListener 拡張クラスです。
040 * このオブジェクトは、HSSFRequest クラスの addListenerForAllRecords メソッドに渡す
041 * HSSFListener インターフェースを実装しています。また、雛形EXCEL を処理後、ExcelLayout
042 * 管理クラスを取得することが出来ます。
043 *
044 * @og.rev 3.8.0.0 (2005/06/07) 新規追加
045 * @og.group 帳票システム
046 *
047 * @version  4.0
048 * @author   Kazuhiko Hasegawa
049 * @since    JDK5.0,
050 */
051public class HybsHSSFListener implements HSSFListener {
052        private SSTRecord sstrec;
053        private int       sheetNo = -1;
054        private int       sheetSize = 0;
055        private boolean   trace  = false;
056
057        private ExcelLayout laynot = null;
058
059        /**
060         * 雛形EXCELの {@カラム} 解析情報を設定します。
061         *
062         * 雛形EXCELは、HSSFListener を使用して、イベント駆動で取得します。その場合、
063         * {@カラム}を含むセルを見つける都度、このメソッドを呼び出して、{@カラム}の
064         * 位置(行列番号)を設定します。
065         * データEXCELからデータを読み出す場合は、ここで登録したカラムの行列より、読み込みます。
066         * 具体的には、HSSFListener#processRecord( Record )で、SSTRecord.sid の 情報をキープしておき、
067         * LabelSSTRecord.sid 毎に、{@カラム}を含むかチェックし、含む場合に、このメソッドに
068         * 解析情報を設定します。
069         *
070         * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応 引数の型変更(SSTRecord の toString() 化)
071         *
072         * @param record Recordオブジェクト
073         */
074        public void processRecord( final Record record ) {
075                switch ( record.getSid() ) {
076                        // Workbook での シート毎に呼ばれます。先行するため、この回数をシート数として数えます。
077                        case BoundSheetRecord.sid:
078                                // 4.0.0.0 (2007/11/29) 入れ子if の統合
079                                if( trace && record instanceof BoundSheetRecord ) {
080                                        BoundSheetRecord bsr = (BoundSheetRecord) record;
081                                        System.out.println("Sheet named: " + bsr.getSheetname() );
082                                }
083                                sheetSize ++ ;
084                                break;
085                        // TYPE_WORKSHEET の場合、シート開始毎に呼ばれます。
086                        case BOFRecord.sid:
087                                if( record instanceof BOFRecord ) {
088                                        BOFRecord bof = (BOFRecord) record;
089                                        if(bof.getType() == BOFRecord.TYPE_WORKBOOK) {
090                                                if( trace ) { System.out.println("Encountered workbook"); }
091                                        }
092                                        else if(bof.getType() == BOFRecord.TYPE_WORKSHEET) {
093                                                // シート開始毎に呼ばれるため、現在のシート番号を得るのに使用します。
094                                                sheetNo++ ;
095                                                if( trace ) { System.out.println("Encountered sheet [" + sheetNo + "]" ); }
096                                        }
097                                }
098                                break;
099                        // すべてのレコードにアクセスするために、一度だけ呼ばれます。ここで初期化しておきます。
100                        case SSTRecord.sid:
101                                if( record instanceof SSTRecord ) {
102                                        sstrec = (SSTRecord) record;
103                                        laynot = new ExcelLayout( sheetSize );
104                                        if( trace ) { System.out.println( "SSTRecord:[" + sstrec.getNumUniqueStrings() + "]" ); }
105                        //              for( int k=0; k<sstrec.getNumUniqueStrings(); k++ )  {
106                        //                      String val = sstrec.getString(k);
107                        //                      System.out.println("SSTRecord id=[" + k + "]=[" + val + "]" );
108                        //              }
109                                }
110                                break;
111                        // 文字型のレコードです。
112                        case LabelSSTRecord.sid:
113                                if( record instanceof LabelSSTRecord ) {
114                                        LabelSSTRecord lrec = (LabelSSTRecord) record;
115
116                                        int   row  = lrec.getRow();
117                                        short col  = lrec.getColumn();
118// POI3.0の場合            String val = sstrec.getString( lrec.getSSTIndex() ) ;
119                                        UnicodeString ustr = sstrec.getString( lrec.getSSTIndex() ) ;
120                                        String val = ustr.getString();
121
122                                        // カラムに、{@を含む場合は、laynot に雛形情報を追加します。
123                                        // 整合性チェックは、ExcelLayoutData クラスで行います。
124                                        if( val != null && val.indexOf( "{@" ) >= 0 ) {
125                                                if( trace ) { System.out.println("String cell id,sheet,row,col=[" + lrec.getSSTIndex() + "," + sheetNo + "," + row + "," + col + "] => " + val ); }
126                                                laynot.addModel( sheetNo,val,row,col );
127                                        }
128                                }
129                                break;
130                        default :
131                                LogWriter.log( "想定外のイベントを受け取りました。=[" + record.getSid() + "]" );
132                                break;
133                }
134        }
135
136        /**
137         * 雛形EXCELシートの {&#064;カラム} 解析データ管理クラスを返します。
138         *
139         * 雛形EXCELを解析し終わった後で、解析データ管理クラスを取り出します。
140         * 雛形EXCELと、データEXCELが1対1ではないため、リアルタイム処理が出来ません。
141         * 一旦、すべての雛形EXCELを解析し終わった後で、データEXCEL処理を行う必要があります。
142         *
143         * @return      雛形EXCELシート
144         */
145        public ExcelLayout getLayout() { return laynot; }
146
147        /**
148         * 雛形EXCEL のシート数を返します。
149         *
150         * @return      雛形シート数
151         */
152        public int getSheetSize() { return sheetSize; }
153
154        /**
155         * 処理経過情報を表示するかどうか[true/false]を指定します(初期値:false)
156         *
157         * イベント毎の状況を、標準出力に出力するかどうかのフラグです。
158         * 初期値は、false です。
159         *
160         * @param       flag    処理経過情報を表示するかどうか[true/false]
161         */
162        public void setTrace( final boolean flag ) { trace = flag; }
163
164        /**
165         * EXCELレイアウト情報を取得します。
166         *
167         * POIのイベント処理(HSSFListener)で、各イベントごとに処理を行います。
168         * イベントは、BOFRecord、BoundSheetRecord、SSTRecord、LabelSSTRecord について
169         * 発生するように設定しています。
170         *
171         * @param       file    処理するEXCELファイル
172         * @param       trace   標準出力にトレース情報を出力するかどうかを指定します。
173         *
174         * @return      EXCELレイアウト情報
175         * @throws IOException 入出力エラーが発生した場合
176         */
177        public static ExcelLayout makeExcelLayout( final File file ,final boolean trace ) throws IOException {
178
179                FileInputStream fin = null;
180                InputStream din = null;
181                final HybsHSSFListener event ;
182                try {
183                        fin = new FileInputStream( file );
184                        POIFSFileSystem poifs = new POIFSFileSystem( fin );
185                        din = poifs.createDocumentInputStream( "Workbook" );
186                        HSSFRequest req = new HSSFRequest();
187
188                        event = new HybsHSSFListener();
189                        event.setTrace( trace );                // 標準出力にトレース情報を出力します。
190
191                //      req.addListenerForAllRecords( event );  // 全イベントを処理
192                        req.addListener( event,BOFRecord.sid );
193                        req.addListener( event,BoundSheetRecord.sid );
194                        req.addListener( event,SSTRecord.sid );
195                        req.addListener( event,LabelSSTRecord.sid );
196
197                        HSSFEventFactory factory = new HSSFEventFactory();
198                        factory.processEvents( req,din );
199                }
200                finally {
201                        Closer.ioClose( fin );          // 4.0.0 (2006/01/31) close 処理時の IOException を無視
202                        Closer.ioClose( din );          // 4.0.0 (2006/01/31) close 処理時の IOException を無視
203        //              fin.close();
204        //              din.close();
205                }
206
207                return event.getLayout();
208        }
209}