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        = "&nbsp;";                                                                // 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             * {&#064;XXXX} をテーブルモ?より読み取り、?をセ?します?
171             * ?ブクラスで実?てください?
172             *
173             * @og.rev 3.0.0.1 (2003/02/14) ?もValueセ?して???に次ペ?ジ要求があった?合?、フォーマットがおかしい
174             * @og.rev 3.0.0.2 (2003/02/20) {&#064;XXXX}?が、EXCELに表示しきれな??合に挿入されるタグの削除処??変更?
175             * @og.rev 3.5.0.0 (2003/09/17) {&#064;XXXX}??スペ?スを?&amp;nbsp;と置き換えます?
176             * @og.rev 3.5.0.0 (2003/09/17) {&#064;XXXX}?がアンバランス時にHybsSystemExceptionを発行する?
177             * @og.rev 3.5.5.9 (2004/06/07) {&#064;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 の ヘッ??/フッター部??、\{\&#064;XXXX\} と、エスケープ文字が付加され?
285             * ので、この??を見つけたら?{&#064;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             * {&#064;XXXX}?変換後?スペ?スを?&amp;nbsp;と置き換えます?
327             *
328             * ただし?式などを使用すると、td タグの属???に{&#064;XXXX}?が含ま?
329             * これに、EXCELのスペ?スである?lt;span style="mso-spacerun:
330             * yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
331             * と置き換えると、属?リスト中のタグと?入れ子状態が発生する為?
332             * これは、置き換えません?
333             * &lt;td XXX&gt;YYY&lt;/td&gt; の YYYの? を置き換えることになります?
334             *
335             * ここでは?去の互換性を最大限確保する為に、特殊な方法で、??ます?
336             * 前後?スペ?スを取り除???で、かつ?つ以上?連続したスペ?ス?
337             * 存在する場合?み、trim して??続スペ?スを?&amp;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"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;
382             * 形式に置き換えます?これは、EXCELがHTML変換する時?ルールです?
383             *
384             * ここでは、スペ?スの個数-1 の &amp;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             * {&#064;QRCODE.XXXX} を含???の alt 属??src 属?にセ?します?
406             *
407             * QRコード?画像を入れ替えるため、alt属?に設定してある キー??を?に?
408             * ?次?ーコード画像を作?し?そ?ファイル名を、src 属?に設定することで?
409             * 動的に画像ファイルのリンクを作?します?
410             * 現在のEXCELでは、バージョンによって?種類?画像表示方法が存在するようで?
411             * ?画像に付き??の変更が?です?こ???は、変換方法が異なる為?
412             * 全く別の処?行う?があります?
413             *
414             * &lt;v:shape ??? alt="{&#064;QRCODE.XXXX}" ???&gt;
415             *   &lt;v:imagedata src="yyy" ???&gt;???&lt;/v:shape&gt;形式とマッチし?
416             * xxx 部?、yyy 部?前方参?します?
417             *
418             * &lt;img ??? src="yyy" ??? alt="{&#064;QRCODE.XXXX}" ??? &gt; 形式とマッチし?
419             * yyy 部?、xxx 部?前方参?します?
420             *
421             * 画像?エンコード?、alt属?に設定した?{&#064;QRCODE.XXXX} ??の
422             * XXXX 部??カラ?ータ(通常、{&#064;XXXX} で取得できる値)を使用します?
423             * ??タが存在しな??合?、src="yyy" 部を削除することで対応します?
424             * なお?後続???関係で、alt="{&#064;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             *  {&#064;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    }