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.HybsSystem;
019 import org.opengion.hayabusa.common.HybsSystemException;
020 import org.opengion.fukurou.util.LogWriter;
021
022 import org.opengion.fukurou.util.QrcodeImage;
023 import org.opengion.fukurou.util.ReplaceString;
024
025 import java.io.IOException;
026 import java.util.Map ;
027 import java.util.HashMap ;
028 import java.util.regex.Pattern;
029 import java.util.regex.Matcher ;
030
031 /**
032 * DBTableReport インターフェース を実?たHTMLをパースするクラスです?
033 * AbstractDBTableReport を継承して?す?で?writeReport() のみオーバ?ライドして??
034 * 固定長?ファイルの出力機?を実現して?す?
035 *
036 * @og.group 帳票シス?
037 *
038 * @version 4.0
039 * @author Kazuhiko Hasegawa
040 * @since JDK5.0,
041 */
042 public class DBTableReport_HTML extends AbstractDBTableReport {
043 private static final String TR_IN = "<tr" ;
044 private static final String TR_OUT = "</tr>" ;
045 private static final String TD_OUT = "</td>" ; // 3.5.5.9 (2004/06/07)
046 private static final String PAGE_BREAK = "page-break" ;
047 private static final String PAGE_END_CUT = "PAGE_END_CUT" ; // 3.6.0.0 (2004/09/17)
048 private static final String END_TAG = "</table></body></html>";
049 private static final String CUT_TAG1 = "<span";
050 private static final String CUT_TAG2 = "</span>";
051 private static final String SPACE_ST = "<span style=\"mso-spacerun: yes\">"; // 3.6.0.0 (2004/09/17)
052 private static final String SPACE = " "; // 3.6.0.0 (2004/09/17)
053 private static final String SPACE_ED = " </span>"; // 3.6.0.0 (2004/09/17)
054 private static final String FRAMESET = "Excel Workbook Frameset" ;
055
056 private static final String CR = System.getProperty("line.separator");
057
058 // <td xxx="yyy">zzzz</td> 形式とマッチし?zzzz< 部?前方参?します?
059 private static final Pattern PTN1 = Pattern.compile("<td[^>]*(>.*?<)/td>");
060 // >aaaa<span bb="cc">dddd</span>eeee< 形式に?文字以上?スペ?スを含?ータと
061 // マッチし、aaaa,dddd,eeee を前方参?します?
062 private static final Pattern PTN2 = Pattern.compile("[^>]*>([^<]*? ++[^<]*?)<");
063 // aa bb cc 形式とマッチし、各連続スペ?ス部?前方参?します?
064 private static final Pattern PTN3 = Pattern.compile("( +)");
065
066 private boolean fileEnd = false; // ファイルの読み取り制御
067
068 // 3.6.1.0 (2005/01/05) QRコー??次?ーコー?用の出力ファイル管?
069 private Map<String,String> qrFileMap = null;
070 // <v:shape ??? alt="{@QRCODE.XXXX}" ???>
071 // <v:imagedata src="yyy" ???>???</v:shape>形式とマッチし?
072 // xxx 部?、yyy 部?前方参?します?
073 private static final Pattern IMGPTN1 = Pattern.compile("<v:shape [^>]*alt=\"\\{@QRCODE.([^\\}]*)\\}\"[^>]*>[^<]*<v:imagedata [^>]*src=\"([^\"]*)\"[^>]*>");
074 // <img ??? src="yyy" ??? alt="{@QRCODE.XXXX}" ??? > 形式とマッチし?
075 // yyy 部?、xxx 部?前方参?します?
076 private static final Pattern IMGPTN2 = Pattern.compile("<img [^>]*src=\"([^\"]*)\"[^>]*alt=\"\\{@QRCODE.([^\\}]*)\\}\"[^>]*>");
077
078 // 4.0.0 (2007/06/08) pageEndCut = true 時? LINE_COPY 機?の実?
079 private static final String LINE_COPY = "LINE_COPY" ; // 4.0.0 (2007/06/08)
080 private String lineCopy = null;
081
082 // 5.7.1.0 (2013/12/06) trueの場?PAGE_END_CUTの判定にdataOver フラグを使用。falseの場合?、rowOver を使用?
083 private boolean USE_DATAOVER = HybsSystem.sysBool( "COMPATIBLE_PAGE_END_CUT_RETRIEVAL" );
084
085 /**
086 * 入力文字? を読み取って、?力します?
087 * tr タグを目印に??trタグ?ずつ取り出します?
088 * 読み取りを終?る?合?、null を返します?
089 * ?ブクラスで実?てください?
090 *
091 * @og.rev 3.0.0.1 (2003/02/14) ?もValueセ?して???に次ペ?ジ要求があった?合?、フォーマットがおかしい
092 * @og.rev 3.6.0.0 (2004/09/24) フォーマットエラーの判?formatErr)を?親クラスに移動します?
093 *
094 * @return 出力文字?
095 */
096 @Override
097 protected String readLine() {
098 if( fileEnd ) { return null; }
099
100 // pageEndCut 時に、データがオーバ?して??のみ、lineCopy があれ?返す?
101 if( pageEndCut && !rowOver && lineCopy != null ) {
102 lineCopyCnt ++ ; // 雛形は、_0 のみが毎回返される為の、加?
103 return lineCopy ;
104 }
105
106 final StringBuilder buf ;
107 try {
108 String line = reader.readLine();
109 if( line == null ) {
110 if( rowOver ) {
111 return null;
112 }
113 else {
114 initReader();
115 initWriter();
116 line = reader.readLine();
117 if( line == null ) { return null; }
118 }
119 }
120 if( line.indexOf( FRAMESET ) >= 0 ) {
121 String errMsg = "HTML ファイルエラー :" + line + HybsSystem.CR
122 + "Excelファイル形式がフレー?なって?す?(?シートには未対?" ;
123 throw new HybsSystemException( errMsg );
124 }
125 if( line.indexOf( TR_IN ) >= 0 ) {
126 buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
127 buf.append( line );
128 int trLebel = 1; // 行を表?<tr> のレベル
129 while( trLebel != 0 ) {
130 line = reader.readLine();
131 // 4.0.0 (2005/08/31) null 参?はずし対?
132 if( line != null ) {
133 if( line.indexOf( TR_IN ) >= 0 ) { trLebel++ ; }
134 if( line.indexOf( TR_OUT ) >= 0 ) { trLebel-- ; }
135 buf.append( CR ).append( line );
136 }
137 else {
138 String errMsg = "HTML ファイルエラー :" + buf.toString() + HybsSystem.CR
139 + "?TR)の整合?が取れる前に、ファイルが終?ました? ;
140 throw new HybsSystemException( errMsg );
141 }
142 }
143 }
144 else {
145 return line;
146 }
147 } catch(IOException ex) {
148 String errMsg = "HTML ファイル 読取時にエラーが発生しました? + reader;
149 throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び?更
150 }
151
152 String rtnLine = buf.toString() ;
153
154 // lineCopy ??の取得?
155 if( pageEndCut && !rowOver ) {
156 // LINE_COPY は削除します?で、表示上見えるよ?しておいてください?
157 int adrs = rtnLine.indexOf( LINE_COPY );
158 if( adrs >= 0 ) {
159 lineCopy = rtnLine.substring( 0,adrs )
160 + rtnLine.substring( adrs + LINE_COPY.length() ) ;
161 rtnLine = lineCopy ;
162 }
163 }
164
165 return rtnLine ;
166 }
167
168 /**
169 * 入力文字? を加工して、?力します?
170 * {@XXXX} をテーブルモ?より読み取り、?をセ?します?
171 * ?ブクラスで実?てください?
172 *
173 * @og.rev 3.0.0.1 (2003/02/14) ?もValueセ?して???に次ペ?ジ要求があった?合?、フォーマットがおかしい
174 * @og.rev 3.0.0.2 (2003/02/20) {@XXXX}?が、EXCELに表示しきれな??合に挿入されるタグの削除処??変更?
175 * @og.rev 3.5.0.0 (2003/09/17) {@XXXX}??スペ?スを?&nbsp;と置き換えます?
176 * @og.rev 3.5.0.0 (2003/09/17) {@XXXX}?がアンバランス時にHybsSystemExceptionを発行する?
177 * @og.rev 3.5.5.9 (2004/06/07) {@XXXX}の連続???アドレス計算方法が?違って?したので修正します?
178 * @og.rev 3.6.0.0 (2004/09/17) pageEndCut ?true の場合?、PAGE_END_CUT ??のある行を削除します?
179 * @og.rev 3.6.0.0 (2004/09/24) フォーマットエラーの判?formatErr)を?親クラスに移動します?
180 * @og.rev 3.6.1.0 (2005/01/05) QRコー??次?ーコー?の機?追?
181 * @og.rev 3.8.1.2 (2005/12/19) PAGE_END_CUTの判定にdataOver フラグを使用?
182 * @og.rev 5.7.1.0 (2013/12/06) USE_DATAOVER ?trueの場?PAGE_END_CUTの判定にdataOver フラグを使用。falseの場合?、rowOver を使用
183 *
184 * @param inLine 入力文字?
185 *
186 * @return 出力文字?
187 */
188 @Override
189 protected String changeData( final String inLine ) {
190 // rowOver で、かつ ペ?ジブレークか?ージエンドカ?の場合?処???
191 if( rowOver && ( inLine.indexOf( PAGE_BREAK ) >= 0 ) ) {
192 fileEnd = true;
193 return END_TAG;
194 }
195
196 String chLine = changeHeaderFooterData( inLine ) ;
197
198 // 3.6.1.0 (2005/01/05) QRコー??次?ーコー?の機?追?
199 if( chLine.indexOf( "{@QRCODE." ) >= 0 ) {
200 chLine = qrcodeReplace( chLine );
201 }
202
203 int st = chLine.indexOf( "{@" );
204 // 3.8.1.2 (2005/12/19) {@XXXX}の存在しな?も PAGE_END_CUTの判定を行う?
205
206 StringBuilder buf = new StringBuilder( chLine );
207
208 boolean spaceInFlag = false; // {@XXXX} 変数の??タにスペ?スを含?ど?チェ?
209 while( st >= 0 ) {
210 int end = buf.indexOf( "}",st+2 );
211
212 // EXCELに表示しきれな?字?、CUT_TAG1,CUT_TAG2 が挿入されてしま??
213 // 削除する?がある?
214 int cutSt1 = buf.indexOf( CUT_TAG1,st+2 );
215 if( cutSt1 >= 0 && cutSt1 < end ) {
216 int cutEnd1 = buf.indexOf( ">",cutSt1 );
217
218 int cutSt2 = buf.indexOf( CUT_TAG2,end );
219 if( cutSt2 >= 0 ) {
220 buf.delete( cutSt2, cutSt2 + CUT_TAG2.length() );
221 }
222 buf.delete( cutSt1, cutEnd1+1 );
223 // 途中をカ?した為、も??計算しなおし?
224 end = buf.indexOf( "}",st+2 ); // 3.5.5.9 (2004/06/07)
225 }
226
227 // 3.5.5.9 (2004/06/07)
228 // 関数等を使用すると、{@XXXX} ??を直接?した??タが?力される?
229 // こ??されたデータは、HTML 表示に使用されるだけ?ため、削除します?
230 // 削除方法?、{@XXX</td> を想定して?為?{@ から </td> の間です?
231 int td_out = buf.indexOf( TD_OUT,st+2 );
232 if( td_out >= 0 && td_out < end ) {
233 buf.delete( st, td_out );
234 // {@XXXX} パラメータが消えた?で、次の計算を行います?
235 st = buf.indexOf( "{@",st+4 ); // 3.5.5.9 (2004/06/07)
236 continue ;
237 }
238
239 // 途中をカ?した為、も??計算しなおし?
240 // フォーマットがおかしい場合?処?
241 if( end < 0 ) {
242 String errMsg = "こ??プレートファイルの {@XXXX} が?フォーマットエラーです?"
243 + HybsSystem.CR
244 + chLine.substring( st ) ;
245 throw new HybsSystemException( errMsg );
246 }
247
248 String key = buf.substring( st+2,end );
249
250 String val = getValue( key );
251 if( val.indexOf( " " ) >= 0 ) { spaceInFlag = true; }
252
253 // {@XXXX} ?実際の値と置き換える?
254 buf.replace( st,end+1,val );
255
256 // {@ の 存在チェ??
257 st = buf.indexOf( "{@",st-1 ); // 3.5.5.9 (2004/06/07)
258 }
259
260 // 3.6.0.0 (2004/09/17) pageEndCut ?true の場合?、PAGE_END_CUT ??のある行を削除します?
261 // ここで判定する?は、PAGE_END_CUT ?そのも?が??されて?可能性があるため?
262 String rtn = buf.toString();
263
264 boolean flag = (USE_DATAOVER) ? dataOver : rowOver ; // 5.7.1.0 (2013/12/06)
265
266 // if( dataOver && pageEndCut ) { // 3.8.1.2 (2005/12/19)
267 if( flag && pageEndCut ) { // 5.7.1.0 (2013/12/06)
268 String temp = rtn.replaceAll( CUT_TAG1 + "[^>]*>" ,"" );
269 if( temp.indexOf( PAGE_END_CUT ) >= 0 ) {
270 rtn = "" ;
271 }
272 }
273 else {
274 // 3.6.0.0 (2004/09/17) スペ?ス置き換え??td XXX>YYY</td> の YYYの?のみとする?
275 if( spaceInFlag ) {
276 rtn = spaceReplace( rtn ) ;
277 }
278 }
279 return rtn ;
280 }
281
282 /**
283 * ?殊???
284 * EXCEL の ヘッ??/フッター部??、\{\@XXXX\} と、エスケープ文字が付加され?
285 * ので、この??を見つけたら?{@XXXX} に、戻して処?るよ?する?
286 *
287 * @param inLine 入力文字?
288 *
289 * @return 出力文字?
290 */
291 private String changeHeaderFooterData( final String inLine ) {
292 int st = inLine.indexOf( "\\{\\@" );
293 if( st < 0 ) { return inLine; }
294
295 StringBuilder buf = new StringBuilder( inLine );
296
297 while( st >= 0 ) {
298 buf.deleteCharAt( st ); // 初めの '\'
299 buf.deleteCharAt( st+1 ); // ?文字削除して?為?1 番目を削除
300 int end = buf.indexOf( "\\}",st+2 );
301 // フォーマットがおかしい場合?処?
302 if( end < 0 ) {
303 String errMsg = "こ??プレート? HeaderFooter 部?? {@XXXX} が?書式エラーです?"
304 + HybsSystem.CR
305 + inLine ;
306 throw new HybsSystemException( errMsg );
307 }
308 buf.deleteCharAt( end ); // 初めの '\'
309 st = buf.indexOf( "\\{\\@",end + 1 );
310 }
311 return buf.toString();
312 }
313
314 /**
315 * 入力文字? を読み取って、?力します?
316 * ?ブクラスで実?てください?
317 *
318 * @param line 入力文字?
319 */
320 @Override
321 protected void println( final String line ) {
322 writer.println( line );
323 }
324
325 /**
326 * {@XXXX}?変換後?スペ?スを?&nbsp;と置き換えます?
327 *
328 * ただし?式などを使用すると、td タグの属???に{@XXXX}?が含ま?
329 * これに、EXCELのスペ?スである?lt;span style="mso-spacerun:
330 * yes">&nbsp;&nbsp;</span>
331 * と置き換えると、属?リスト中のタグと?入れ子状態が発生する為?
332 * これは、置き換えません?
333 * <td XXX>YYY</td> の YYYの? を置き換えることになります?
334 *
335 * ここでは?去の互換性を最大限確保する為に、特殊な方法で、??ます?
336 * 前後?スペ?スを取り除???で、かつ?つ以上?連続したスペ?ス?
337 * 存在する場合?み、trim して??続スペ?スを?&nbsp;と置き換えます?
338 * ??間に連続スペ?スがな??合?、前後?スペ?スも削除せずに?
339 * ????をそのまま返します?
340 * 前後?スペ?スを変換してしま?、数字型の場合に、EXCELでの計算式がエラーになります?
341 *
342 * @og.rev 3.5.0.0 (2003/09/17) 新規追?
343 * @og.rev 3.5.5.0 (2004/03/12) 連続スペ?スの処?EXCELの方式に合わせる
344 * @og.rev 3.6.0.0 (2004/09/17) スペ?ス置き換え??td XXX>YYY</td> の YYYの?のみとする?
345 * @og.rev 3.6.1.0 (2005/01/05) 置換ロジ?修正(ReplaceString クラスを使用)
346 *
347 * @param target ????
348 *
349 * @return 置換えた文字?
350 */
351 private String spaceReplace( final String target ) {
352 ReplaceString repData = new ReplaceString();
353
354 Matcher match1 = PTN1.matcher( target ) ;
355 while( match1.find() ) {
356 int st1 = match1.start(1);
357 String grp1 = match1.group(1);
358 Matcher match2 = PTN2.matcher( grp1 ) ;
359 while( match2.find() ) {
360 int st2 = match2.start(1);
361 String grp2 = match2.group(1);
362 Matcher match3 = PTN3.matcher( grp2 ) ;
363 while( match3.find() ) {
364
365 int st = st1 + st2 + match3.start(1);
366 int ed = st1 + st2 + match3.end(1);
367
368 repData.add( st,ed,makeSpace( ed-st ) );
369 }
370 }
371 }
372
373 String rtn = repData.replaceAll( target );
374
375 return rtn ;
376 }
377
378 /**
379 * ??個数のスペ?ス?を表す?EXCEL の記号を作?します?
380 *
381 * EXCELでは、スペ?ス??以上を?lt;span style="mso-spacerun: yes">&nbsp;&nbsp;</span>
382 * 形式に置き換えます?これは、EXCELがHTML変換する時?ルールです?
383 *
384 * ここでは、スペ?スの個数-1 の &nbsp; を持つ、上記???を作?します?
385 * ???は、本物のスペ?ス記号を割り当てます?
386 *
387 * @og.rev 3.6.0.0 (2004/09/17) 新規追?
388 *
389 * @param cnt スペ?スの個数
390 *
391 * @return 置換えた文字?
392 */
393 private String makeSpace( final int cnt ) {
394 StringBuilder buf = new StringBuilder( 40 + cnt * 6 );
395 buf.append( SPACE_ST );
396 for( int i=1; i<cnt; i++ ) {
397 buf.append( SPACE );
398 }
399 buf.append( SPACE_ED );
400
401 return buf.toString();
402 }
403
404 /**
405 * {@QRCODE.XXXX} を含???の alt 属??src 属?にセ?します?
406 *
407 * QRコード?画像を入れ替えるため、alt属?に設定してある キー??を?に?
408 * ?次?ーコード画像を作?し?そ?ファイル名を、src 属?に設定することで?
409 * 動的に画像ファイルのリンクを作?します?
410 * 現在のEXCELでは、バージョンによって?種類?画像表示方法が存在するようで?
411 * ?画像に付き??の変更が?です?こ???は、変換方法が異なる為?
412 * 全く別の処?行う?があります?
413 *
414 * <v:shape ??? alt="{@QRCODE.XXXX}" ???>
415 * <v:imagedata src="yyy" ???>???</v:shape>形式とマッチし?
416 * xxx 部?、yyy 部?前方参?します?
417 *
418 * <img ??? src="yyy" ??? alt="{@QRCODE.XXXX}" ??? > 形式とマッチし?
419 * yyy 部?、xxx 部?前方参?します?
420 *
421 * 画像?エンコード?、alt属?に設定した?{@QRCODE.XXXX} ??の
422 * XXXX 部??カラ?ータ(通常、{@XXXX} で取得できる値)を使用します?
423 * ??タが存在しな??合?、src="yyy" 部を削除することで対応します?
424 * なお?後続???関係で、alt="{@QRCODE.XXXX}" ??は、削除します?
425 *
426 * @og.rev 3.6.1.0 (2005/01/05) 新規追?
427 *
428 * @param target ????
429 *
430 * @return 置換えた文字?
431 */
432 private String qrcodeReplace( final String target ) {
433 ReplaceString repData = new ReplaceString();
434
435 Matcher match1 = IMGPTN1.matcher( target ) ;
436 while( match1.find() ) {
437 String altV = match1.group(1);
438
439 int stAlt = match1.start(1) - 9 ; // {@QRCODE. まで遡?
440 int edAlt = match1.end(1) + 1 ; // } を含める
441 repData.add( stAlt,edAlt,"" ); // {@QRCODE.XXXX} の部?除
442
443 int st = match1.start(2);
444 int ed = match1.end(2);
445
446 String msg = getValue( altV ); // QRコード変換する??の取?
447 if( msg != null && msg.length() > 0 ) {
448 String newStr = makeQrImage( altV,msg ); // 画像ファイルのファイル?
449 repData.add( st,ed,newStr );
450 }
451 else {
452 repData.add( st-5,ed+1,"" ); // src="yyy" 部??み削除
453 }
454 }
455
456 Matcher match2 = IMGPTN2.matcher( target ) ;
457 while( match2.find() ) {
458 int st = match2.start(1);
459 int ed = match2.end(1);
460
461 String altV = match2.group(2);
462 int stAlt = match2.start(2) - 9 ; // {@QRCODE. まで遡?
463 int edAlt = match2.end(2) + 1 ; // } を含める
464 repData.add( stAlt,edAlt,"" ); // {@QRCODE.XXXX} の部?除
465
466 String msg = getValue( altV ); // QRコード変換する??の取?
467 if( msg != null && msg.length() > 0 ) {
468 String newStr = makeQrImage( altV,msg ); // 画像ファイルのファイル?
469 repData.add( st,ed,newStr );
470 }
471 else {
472 repData.add( st-5,ed+1,"" ); // src="yyy" 部??み削除
473 }
474 }
475
476 String rtn = repData.replaceAll( target ) ;
477
478 return rtn ;
479 }
480
481 /**
482 * ??カラ?と、QRコード変換する??より、画像を作?します?
483 *
484 * 返り値は、作?した画像ファイルのファイル名です?
485 * これは、データが存在しな??合に、src="" を返す?があるため?
486 * (でな?、画像へのリンクが表示されてしま??)
487 * src="./帳票ID.files/image00x.png" と?画像ファイルのアドレス部?
488 * {@QRCODE_カラ?} 形式に変更しておく?があります?
489 *
490 * @og.rev 3.6.1.0 (2005/01/05) 新規追?
491 *
492 * @param key カラ?
493 * @param msg QRコード変換する??
494 *
495 * @return 画像ファイルのファイル?
496 */
497 private String makeQrImage( final String key, final String msg ) {
498 if( msg == null || msg.length() == 0 ) { return "" ; }
499
500 String realClmName = null ;
501 int sp = key.lastIndexOf( '_' );
502 if( sp >= 0 ) {
503 try {
504 int row = Integer.parseInt( key.substring( sp+1 ) );
505 int realRow = getRealRow( row );
506 realClmName = key.substring( 0,sp ) + "_" + realRow ;
507 }
508 catch (NumberFormatException e) { // 4.0.0 (2005/01/31)
509 String errMsg = "警告:QRCODE名?ヘッ??に'_'カラ?が使用";
510 LogWriter.log( errMsg );
511 }
512 }
513 else {
514 realClmName = key ;
515 }
516
517 if( qrFileMap == null ) { qrFileMap = new HashMap<String,String>(); }
518 if( qrFileMap.containsKey( realClmName ) ) { // Map にすでに存在して??
519 return qrFileMap.get( realClmName );
520 }
521
522 // 帳票?? を?に、画像ファイルの保存フォル?求めます?
523 String filename = "./" + listId + ".files/" + realClmName + ".png";
524 String fullAddress = htmlDir + filename ;
525
526 QrcodeImage qrImage = new QrcodeImage();
527 qrImage.init( msg,fullAddress );
528 qrImage.saveImage();
529
530 qrFileMap.put( realClmName,filename );
531 return filename;
532 }
533 }