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