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の {@カラ? 解析情報を設定します?
073 *
074 * 雛形EXCELは、HSSFListener を使用して、イベント?で取得します?そ?場合?
075 * {@カラ?を含?ルを見つける都度、このメソ?を呼び出して、{@カラ?の
076 * 位置(行?番号)を設定します?
077 * ??タEXCELから??タを読み出す?合?、ここで登録したカラ??行?より、読み込みます?
078 * 具体的には、HSSFListener#processRecord( Record )で、SSTRecord.sid の ??をキープしておき?
079 * LabelSSTRecord.sid 毎に、{@カラ?を含?チェ?し?含??合に、このメソ?に
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の {@カラ? 解析情報(ExcelLayoutData)を?列で取得します?
093 *
094 * 雛形EXCELは、イベント??取り込?、すべての処?終?てから、このメソ?で
095 * 処?果を取り?す?があります?
096 * 解析情報は、ExcelLayoutData オブジェクトにシート単位に保管されて?す?
097 * こ? ExcelLayoutData オブジェク?ひとつに、{@カラ? ひとつ、つまり?
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 }