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