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     */
016    package org.opengion.hayabusa.report;
017    
018    import org.opengion.hayabusa.common.HybsSystemException;
019    
020    import java.util.Map;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.ArrayList;
024    import java.util.Iterator ;
025    import java.util.NoSuchElementException;
026    import java.util.Arrays ;
027    
028    /**
029     * 【EXCEL取込】雛形EXCELシートã? {@カラãƒ? 解析データを管ç�??収集する 雛形レイアウト管ç�?‚¯ラスですã?
030     * POIのHSSFListener などでã€?››形æƒ??を収é›?�—、HSSFSheet などでã€?››形æƒ??のアドレス(行å?)から
031     * å¿?¦�なæƒ??を取得し、このオブジェクトに設定しておきますã?
032     * EXCELシート毎に、INSERTæ–?�¨、対応する文字å?配å?を取りå?しますã?
033     *
034     * @og.rev 3.8.0.0 (2005/06/07) 新規追åŠ?
035     * @og.group 帳票シスãƒ?ƒ 
036     *
037     * @version  4.0
038     * @author   Kazuhiko Hasegawa
039     * @since    JDK5.0,
040     */
041    public class ExcelLayout {
042    
043            private final Map<String,String> headMap  = new HashMap<String,String>();   // シート単位ã?ヘッãƒ??キーをæ?納しますã?
044            private final Map<String,String> bodyMap  = new HashMap<String,String>();   // シート単位ã?ボディーキーをæ?納しますã?
045            private final Map<Integer,Map<String,String>> dataMap  = new HashMap<Integer,Map<String,String>>();     // シート単位ã?ãƒ??タをæ?納するMapをæ?納しますã?(キーは、GEEDNO)
046    
047            private final List<ExcelLayoutData>[] model  ;    // シート毎にExcelLayoutDataがæ?納されますã?
048    
049            private String loopClm = null;                                          // 繰返å¿??カラãƒ?なければnull))
050            private ExcelLayoutDataIterator iterator = null;        // ExcelLayoutData を返す、Iterator
051    
052            /**
053             * コンストラクター
054             *
055             * 雛形のæœ?¤§シート数を設定しますã?
056             * ここではã€??番で管ç�?�—てã�?‚‹為、その雛形シート番号がå?ç�?¯¾象外であってもã?
057             * 雛形EXCEL上に存在するシート数を設定するå¿?¦�がありますã?
058             * 具体的には、HSSFListener#processRecord( Record )で、BoundSheetRecord.sid の
059             * イベントã?数を数えて設定しますã?
060             *
061             * @param       sheetSize       æœ?¤§シート数
062             */
063            @SuppressWarnings(value={"unchecked","rawtypes"})
064            public ExcelLayout( final int sheetSize ) {
065                    model = new ArrayList[sheetSize];
066                    for( int i=0; i<sheetSize; i++ ) {
067                            model[i] = new ArrayList<ExcelLayoutData>();
068                    }
069            }
070    
071            /**
072             * 雛形EXCELの {&#064;カラãƒ? 解析情報を設定しますã?
073             *
074             * 雛形EXCELは、HSSFListener を使用して、イベントé§?‹•で取得しますã?そã?場合ã?
075             * {&#064;カラãƒ?を含ã‚?‚»ルを見つける都度、このメソãƒ?ƒ‰を呼び出して、{&#064;カラãƒ?の
076             * 位置(行å?番号)を設定しますã?
077             * ãƒ??タEXCELからãƒ??タを読み出すå?合ã?、ここで登録したカラãƒ??行å?より、読み込みますã?
078             * 具体的には、HSSFListener#processRecord( Record )で、SSTRecord.sid の æƒ??をキープしておきã€?
079             * LabelSSTRecord.sid 毎に、{&#064;カラãƒ?を含ã‚?�‹チェãƒ?‚¯しã?含ã‚??合に、このメソãƒ?ƒ‰に
080             * 解析情報を設定しますã?
081             *
082             * @param       sheetNo シート番号
083             * @param       key             処ç�?‚«ラãƒ?
084             * @param       rowNo   行番号
085             * @param       colNo   列番号
086             */
087            public void addModel( final int sheetNo, final String key, final int rowNo, final short colNo ) {
088                    model[sheetNo].add( new ExcelLayoutData( key,rowNo,colNo ) );
089            }
090    
091            /**
092             * 雛形EXCELの {&#064;カラãƒ? 解析情報(ExcelLayoutData)をé?列で取得しますã?
093             *
094             * 雛形EXCELは、イベントå?ç�?�§取り込ã‚?‚º、すべての処ç�?�Œ終äº?�—てから、このメソãƒ?ƒ‰で
095             * 処ç�?µ�果を取りå?すå¿?¦�がありますã?
096             * 解析情報は、ExcelLayoutData オブジェクトにシート単位に保管されてã�?�¾すã?
097             * こã? ExcelLayoutData オブジェクãƒ?ひとつに、{&#064;カラãƒ? ひとつ、つまりã?
098             * ある特定ã?行å?番号を持ってã�?�¾すã?
099             * ãƒ??タEXCELを読取る場合ã?こã? ExcelLayoutData配å?から、行å?æƒ??を取りå?しã?
100             * addData メソãƒ?ƒ‰で、キーæƒ??と関連付けて登録する為に、使用しますã?
101             *
102             * @param       sheetNo シート番号
103             * @param       loopClm 繰返å¿??カラãƒ?なければ通常の?‘対?‘å?ç�?
104             *
105             * @return      ExcelLayoutData配å?
106             */
107            public Iterator<ExcelLayoutData> getLayoutDataIterator( final int sheetNo, final String loopClm ) {
108                    this.loopClm = loopClm ;
109                    ExcelLayoutData[] datas = model[sheetNo].toArray( new ExcelLayoutData[model[sheetNo].size()] );
110                    iterator = new ExcelLayoutDataIterator( datas,loopClm );
111                    return iterator ;
112            }
113    
114            /**
115             * 解析情報(clm,edbn)と関連付けて、データEXCELの値を設定しますã?
116             *
117             * ãƒ??タEXCELはã€?››形EXCELの解析情報をå?に、行å?番号から設定å?を取りå?しますã?
118             * そã?設定å?は、取りだした ExcelLayoutData の clm,edbn と関連付けて、このメソãƒ?ƒ‰で登録しますã?
119             * こã?処ç�??、シート毎に、å?期化して使ã�?¿?¦�がありますã?
120             * 初期化メソãƒ?ƒ‰する場合ã?、dataClear() を呼び出してくださいã€?
121             *
122             * @param       clm             カラãƒ?��
123             * @param       edbn    枝番
124             * @param       value   ãƒ??タ値
125             */
126            public void addData( final String clm, final int edbn, final String value ) {
127                    if( loopClm != null && loopClm.equals( clm ) && edbn >= 0 && ( value == null || value.length() == 0 ) ) {
128                            iterator.setEnd();
129                            Integer edbnObj = Integer.valueOf( edbn );
130                            dataMap.remove( edbnObj );              // 枝番単位ã?Mapを削除
131                            return ;
132                    }
133    
134                    Integer edbnObj = Integer.valueOf( edbn );
135                    Map<String,String> map = dataMap.get( edbnObj );          // 枝番単位ã?Mapを取å¾?
136                    if( map == null ) { map = new HashMap<String,String>(); }
137                    map.put( clm,value );                           // 枝番に含まれるキーと値をセãƒ?ƒˆ
138                    dataMap.put( edbnObj,map );                     // そã?Mapを枝番に登録
139    
140                    if( edbn < 0 ) {
141                            headMap.put( clm,null );
142                    }
143                    else {
144                            bodyMap.put( clm,null );
145                    }
146            }
147    
148            /**
149             * ãƒ??タEXCELの設定情報をå?期化しますã?
150             *
151             * ãƒ??タEXCELとã€?››形EXCELの解析情報を関連付けるå?ç�??、シート毎に行うå¿?¦�がありますã?
152             * 処ç�?µ‚äº?™‚(シートå?り替え時)こã?メソãƒ?ƒ‰を呼び出して、å?期化しておくå¿?¦�がありまã�?
153             *
154             */
155            public void dataClear() {
156                    dataMap.clear();
157                    headMap.clear();
158                    bodyMap.clear();
159            }
160    
161            /**
162             * ヘッãƒ??æƒ??のINSERT用Queryæ–?­—å?を取得しますã?
163             *
164             * シート単位に、データEXCELより、INSERT用のQueryæ–?­—å?を作æ?しますã?
165             * こã?、Query は、シート単位に登録したキーæƒ??のæœ?¤§数(使用されてã�?‚‹すべてのキー)ã‚?
166             * å…?�«、PreparedStatement で処ç�?�§きる形の INSERTæ–?‚’作æ?しますã?
167             * シート単位に呼び出すå¿?¦�がありますã?
168             *
169             * @param       table   ヘッãƒ??æƒ??を登録するãƒ??タベã?スå�?HEADERDBID)
170             *
171             * @return      ヘッãƒ??æƒ??のINSERT用Queryæ–?­—å?
172             */
173            public String getHeaderInsertQuery( final String table ) {
174                    if( table == null || table.length() == 0 || headMap.isEmpty() ) { return null; }
175                    return makeQuery( table,headMap );
176            }
177    
178            /**
179             * ボディ(明細)æƒ??のINSERT用Queryæ–?­—å?を取得しますã?
180             *
181             * シート単位に、データEXCELより、INSERT用のQueryæ–?­—å?を作æ?しますã?
182             * こã?、Query は、シート単位に登録したキーæƒ??のæœ?¤§数(使用されてã�?‚‹すべてのキー)ã‚?
183             * å…?�«、PreparedStatement で処ç�?�§きる形の INSERTæ–?‚’作æ?しますã?
184             * シート単位に呼び出すå¿?¦�がありますã?
185             *
186             * @param       table   ボディ(明細)æƒ??を登録するãƒ??タベã?スå�?BODYDBID)
187             *
188             * @return      ボディ(明細)æƒ??のINSERT用Queryæ–?­—å?
189             */
190            public String getBodyInsertQuery( final String table ) {
191                    if( table == null || table.length() == 0 || bodyMap.isEmpty() ) { return null; }
192                    return makeQuery( table,bodyMap );
193            }
194    
195            /**
196             * ヘッãƒ??æƒ??のINSERT用Queryに対応するã?ãƒ??タ配å?を取得しますã?
197             *
198             * getHeaderInsertQuery( String ) で取りã�?�—ã�?PreparedStatement に設定するå?配å?ですã?
199             * シート単位に呼び出すå¿?¦�がありますã?
200             *
201             * @param       systemId        シスãƒ?ƒ ID(SYSTEM_ID)
202             * @param       ykno    要求番号(YKNO)
203             * @param       sheetNo 登録するãƒ??タEXCELのシート番号(SHEETNO)
204             *
205             * @return      ãƒ??タ配å?
206             */
207            public String[] getHeaderInsertData( final String systemId,final int ykno,final int sheetNo ) {
208                    String[] keys = headMap.keySet().toArray( new String[headMap.size()] );
209                    if( keys == null || keys.length == 0 ) { return new String[0]; }
210    
211                    Integer edbnObj = Integer.valueOf( -1 );                // ヘッãƒ??
212                    Map<String,String> map = dataMap.get( edbnObj );
213                    if( map == null ) { return new String[0]; }
214    
215                    String[] rtnData = new String[keys.length+4];
216    
217                    rtnData[0] = systemId;
218                    rtnData[1] = String.valueOf( ykno );
219                    rtnData[2] = String.valueOf( sheetNo );
220                    rtnData[3] = String.valueOf( -1 );              // 枝番
221    
222                    for( int i=0; i<keys.length; i++ ) {
223                            rtnData[i+4] = map.get( keys[i] );
224                    }
225    
226                    return rtnData;
227            }
228    
229            /**
230             * ボディ(明細)æƒ??のINSERT用Queryに対応するã?ãƒ??タ配å?のリスãƒ?String[] のList)を取得しますã?
231             *
232             * getHeaderInsertQuery( String ) で取りã�?�—ã�?PreparedStatement に設定するå?配å?ですã?
233             * シート単位に呼び出すå¿?¦�がありますã?
234             *
235             * @param       systemId        シスãƒ?ƒ ID(SYSTEM_ID)
236             * @param       ykno    要求番号(YKNO)
237             * @param       sheetNo 登録するãƒ??タEXCELのシート番号(SHEETNO)
238             *
239             * @return      ãƒ??タ配å?のリスãƒ?
240             */
241            public List<String[]> getBodyInsertData( final String systemId,final int ykno,final int sheetNo ) {
242                    String[] keys = bodyMap.keySet().toArray( new String[bodyMap.size()] );
243                    if( keys == null || keys.length == 0 ) { return null; }
244    
245                    List<String[]> rtnList = new ArrayList<String[]>();
246    
247                    Integer[] edbnObjs = dataMap.keySet().toArray( new Integer[dataMap.size()] );
248                    for( int i=0; i<edbnObjs.length; i++ ) {
249                            int edbn = edbnObjs[i].intValue();
250                            if( edbn < 0 ) { continue; }         // ヘッãƒ??の場合ã?、読み直ã�?
251    
252                            String[] rtnData = new String[keys.length+4];   // 毎回、新規に作æ?するã€?
253                            rtnData[0] = systemId;
254                            rtnData[1] = String.valueOf( ykno );
255                            rtnData[2] = String.valueOf( sheetNo );
256                            rtnData[3] = String.valueOf( edbn );    // 枝番
257    
258                            Map<String,String> map = dataMap.get( edbnObjs[i] );
259                            for( int j=0; j<keys.length; j++ ) {
260                                    rtnData[j+4] = map.get( keys[j] );
261                            }
262                            rtnList.add( rtnData );
263                    }
264    
265                    return rtnList;
266            }
267    
268            /**
269             * å†?ƒ¨æƒ??Mapより、INSERT用Queryæ–?­—å?を取得しますã?
270             *
271             * シート単位に、データEXCELより、INSERT用のQueryæ–?­—å?を作æ?しますã?
272             * こã?、Query は、シート単位に登録したキーæƒ??のæœ?¤§数(使用されてã�?‚‹すべてのキー)ã‚?
273             * å…?�«、PreparedStatement で処ç�?�§きる形の INSERTæ–?‚’作æ?しますã?
274             * シート単位に呼び出すå¿?¦�がありますã?
275             *
276             * @param       table   ãƒ??ブルå�?
277             * @param       map             ボディ(明細)æƒ??を登録するå†?ƒ¨æƒ??Map
278             *
279             * @return      INSERT用Queryæ–?­—å?
280             */
281            private String makeQuery( final String table,final Map<String,String> map ) {
282                    String[] keys = map.keySet().toArray( new String[map.size()] );
283    
284                    if( keys == null || keys.length == 0 ) { return null; }
285    
286                    StringBuilder buf1 = new StringBuilder( 200 );
287                    buf1.append( "INSERT INTO " ).append( table ).append( " ( " );
288                    buf1.append( "GESYSTEM_ID,GEYKNO,GESHEETNO,GEEDNO" );
289    
290                    StringBuilder buf2 = new StringBuilder( 200 );
291                    buf2.append( " ) VALUES (" );
292                    buf2.append( "?,?,?,?" );
293    
294                    for( int i=0; i<keys.length; i++ ) {
295                            buf1.append( "," ).append( keys[i] );
296                            buf2.append( ",?" );
297                    }
298                    buf2.append( ")" );
299    
300                    buf1.append( buf2 );
301    
302                    return buf1.toString();
303            }
304    }
305    
306    /**
307     * ExcelLayoutData (雛形解析結果)のシート毎ã?Iteratorを返しますã?
308     * ExcelLayout では、データEXCELは、シート毎に解析しますã?
309     * 通常はã€?››形とãƒ??タは?‘対?‘ã?関係でã€?››形より多いãƒ??タは、読み取りませんしã?
310     * 少なã�?ƒ‡ータは、NULL値でãƒ??タ登録しますã?
311     * ここで、繰返å¿??カラãƒ?LOOPCLM)を指定することで、指定ã?カラãƒ?�Œå¿??であることを利用してã€?
312     * ãƒ??タが少なã�??合ã?、そこまでで処ç�?‚’中止して、データが多い場合ã?、仮想çš?�«カラãƒ?�Œ
313     * 存在すると仮定してã€?››形に存在しなã�?®?‰€のãƒ??タを読み取れるよã�?�«、Iterator を返しますã?
314     * ãƒ??タがオーバã?する場合ã?、仮想çš?�«カラãƒ??存在するアドレスを求めるå¿?¦�があるためã€?
315     * æœ?½?カラãƒ?0 と カラãƒ?1 がå¿?¦�ですã?さらに、各カラãƒ??、行方向に並んでおりã€?
316     * 列方向ã?、同ä¸?�§あるとã�?�†前提で、読み取るべき行å?番号を作æ?しますã?
317     *
318     * @og.rev 3.8.0.0 (2005/06/07) 新規追åŠ?
319     * @og.group 帳票シスãƒ?ƒ 
320     *
321     * @version  4.0
322     * @author   Kazuhiko Hasegawa
323     * @since    JDK5.0,
324     */
325    class ExcelLayoutDataIterator implements Iterator<ExcelLayoutData> {
326            private final ExcelLayoutData[] layoutDatas ;
327            private final String    loopClm ;
328            private int             incSize = 1;    // 行番号の増加数(段çµ?�ªどの場合ã?ã€?¼‘以上となã‚?
329            private int             count   = 0;    // 現在処ç�?¸­の行番号
330            private int             edbnCnt = 0;    // 処ç�?¸­の枝番に相当するカウントå?
331            private int             stAdrs  = -1;   // 繰返し処ç�?‚’行う開始アドレス
332            private int             edAdrs  = -1;   // 繰返し処ç�?‚’行う終äº?‚¢ドレス
333    
334            /**
335             * ExcelLayoutData の配å?を受け取って、å?期情報を設定しますã?
336             *
337             * 繰返å¿??カラãƒ?LOOPCLM)がnullでなã�??合ã?枝番が0ã?カラãƒ?‚’繰り返しますã?
338             * 繰り返す場合ã?行番号と枝番を指定して、既存ã?ExcelLayoutDataオブジェクトを作æ?しã?
339             * 仮想çš?�«繰返しますã?
340             *
341             * @param datas ExcelLayoutData[]       ExcelLayoutData の配å?
342             * @param       lpClm   繰返å¿??カラãƒ?LOOPCLM)
343             */
344            public ExcelLayoutDataIterator( final ExcelLayoutData[] datas,final String lpClm ) {
345                    layoutDatas = datas;
346                    loopClm     = lpClm;
347    
348                    int size    = layoutDatas.length;               // 配å?のæœ?¤§値
349    
350                    Arrays.sort( layoutDatas );             // 枝番é ?�«ソートされますã?
351                    // loopClm を使ã�??合ã?、枝番 -1(ヘッãƒ?とã€?¼�ã?ãƒ??タのみを使用する。枝番?‘ã?、増加数の取得ã?みに用ã�?‚‹ã€?
352                    if( loopClm != null ) {
353                            int zeroRow = -1;
354                            for( int i=0; i<size; i++ ) {
355                                    // System.out.println( "count=" + i + ":" + layoutDatas[i] );
356                                    int edbn = layoutDatas[i].getEdbn();
357                                    if( stAdrs < 0 && edbn == 0 ) { stAdrs = i; }        // 初ã?枝番0アドレス?�開å§?含ã‚?
358                                    if( edAdrs < 0 && edbn == 1 ) { edAdrs = i; }        // 初ã?枝番1アドレス?�終äº?含まなã�?
359                                    if( loopClm.equals( layoutDatas[i].getClm() ) ) {
360                                            if( edbn == 0 ) {
361                                                    zeroRow = layoutDatas[i].getRowNo();    // loopClm の枝番0 の行番号
362                                            }
363                                            else if( edbn == 1 ) {
364                                                    incSize = layoutDatas[i].getRowNo() - zeroRow;  // 増加数=枝番1-枝番0
365                                                    break;
366                                            }
367                                    }
368                            }
369                            // 繰返がある場å�?枝番ã�?以ä¸?でloopClmが見つからなã�??合ã?エラー
370                            if( zeroRow < 0 && stAdrs >= 0 ) {
371                                    String errMsg = "繰返å¿??カラãƒ?�Œシート中に存在しませんã€?" + loopClm + "]";
372                                    throw new HybsSystemException( errMsg );
373                            }
374                    }
375                    if( stAdrs < 0 ) { stAdrs = 0; }     // 開å§?含ã‚?
376                    if( edAdrs < 0 ) { edAdrs = size; }  // 終äº?含まなã�?
377    //              System.out.println( "stAdrs=" + stAdrs + " , edAdrs=" + edAdrs  );
378            }
379    
380            /**
381             * 繰り返し処ç�?�§さらに要ç´?�Œある場合に true を返しますã?
382             * つまりã?next が例外をスローしなã�?�§要ç´?‚’返す場合に true を返しますã?
383             *
384             * @return      反復子がさらに要ç´?‚’持つ場合ã? true
385             */
386            public boolean hasNext() {
387                    if( loopClm != null && count == edAdrs ) {
388                            count = stAdrs;
389                            edbnCnt++;
390                    }
391    //              System.out.print( "count=[" + count + "]:" );
392                    return ( count < edAdrs );
393            }
394    
395            /**
396             * 繰り返し処ç�?�§次の要ç´?‚’返しますã?
397             *
398             * @return Object 繰り返し処ç�?�§次の要ç´?
399             * @throws NoSuchElementException 繰り返し処ç�?�§それ以上要ç´?�Œなã�??å�?
400             */
401            public ExcelLayoutData next() throws NoSuchElementException {
402                    if( layoutDatas == null || layoutDatas.length == count ) {
403                            String errMsg = "行番号がレイアウトデータをオーバã?しましたã€? +
404                                                    " 行番号=[" + count + "]" ;
405                            throw new NoSuchElementException( errMsg );
406                    }
407    
408                    ExcelLayoutData data = layoutDatas[count++];
409    
410                    if( edbnCnt > 0 ) {  // 繰返å¿??é ?›®機è?が働ã�?�¦ã�?‚‹ケース
411                            int rowNo = data.getRowNo() + edbnCnt * incSize ;
412                            data = data.copy( rowNo,edbnCnt );
413    //                      System.out.println( "row,edbn=[" + rowNo + "," + edbnCnt + "]:" + data );
414                    }
415    
416                    return data;
417            }
418    
419            /**
420             * こã?メソãƒ?ƒ‰は、このクラスからは使用できませんã€?
421             * ※ こã?クラスでは実è£?�•れてã�?�¾せんã€?
422             * こã?メソãƒ?ƒ‰では、å¿?�š、UnsupportedOperationException がã?throw されますã?
423             */
424            public void remove() {
425                    String errMsg = "こã?メソãƒ?ƒ‰は、このクラスからは使用できませんã€?;
426                    throw new UnsupportedOperationException( errMsg );
427            }
428    
429            /**
430             * 繰返し処ç�?‚’終äº?�•せますã?
431             *
432             */
433            public void setEnd() {
434                    edAdrs = -1;
435            }
436    }