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 */
016package org.opengion.hayabusa.filter;
017
018import org.opengion.fukurou.util.StringUtil;
019import org.opengion.fukurou.util.FileUtil;
020
021import java.util.regex.Pattern ;
022import java.util.regex.Matcher ;
023import java.util.Set ;
024import java.util.HashSet ;
025import java.io.File;
026
027/**
028 * FileFilter で使用する、紙芝居用HTMLファイル作成時に内部文字列を変換するクラスです。
029 *
030 * @og.group フィルター処理
031 *
032 * @version  4.0
033 * @author   Kazuhiko Hasegawa
034 * @since    JDK5.0,
035 */
036public class FileResponseTransform {
037
038        // 5.6.4.2 (2013/05/17) JFreeChart の画像ファイル(ChartTempフォルダ) のコピー関係の固定値
039        private static final String CHART_KEY = "/ChartTemp/" ;
040        private static final int    KEY_LEN   = CHART_KEY.length();
041
042        private static final Set<String> TARGET_CHANGE_SET = new HashSet<>();
043
044        private static final ChangeData[] CHG_DATA = new ChangeData[] {                                                                                 // 6.4.1.1 (2016/01/16) data → CHG_DATA refactoring
045                 new ChangeData( null , "\"/[^/]*/jsp/"                         ,"\"../" )                              // 5.5.7.2 (2012/10/09) マッチ条件を広げる
046                ,new ChangeData( null , "'/[^/]*/jsp/"                          ,"'../" )                               // 5.5.7.2 (2012/10/09) マッチ条件を広げる
047                ,new ChangeData( null , "\\(/[^/]*/jsp/"                        ,"(../" )                               // 6.4.3.2 (2016/02/19) マッチ条件を広げる
048                ,new ChangeData( null , "=\"/[^/]*/[^/]*/ChartTemp/","=\"../ChartTemp/" )       // 5.5.2.5 (2012/05/21) JfreeChart関係の画像のアドレス変換
049                ,new ChangeData( null , "='/[^/]*/[^/]*/ChartTemp/"     ,"='../ChartTemp/" )    // 5.5.2.5 (2012/05/21) JfreeChart関係の画像のアドレス変換
050                ,new ChangeData( null , "=\"/[^/]*/help/"                       ,"=\"../help/" )                // 5.5.2.5 (2012/05/21) help関係の画像のアドレス変換
051                ,new ChangeData( null , "='/[^/]*/help/"                        ,"='../help/" )                 // 5.5.2.5 (2012/05/21) help関係の画像のアドレス変換
052                ,new ChangeData( null , "\\.jsp"                                        ,".htm" )
053                ,new ChangeData( "query.htm"    , "action=\"result"                     ,"action=\"forward" )                                   // 6.2.5.0 (2015/06/05) GMIS V6 対応(3ペイン対応)
054                ,new ChangeData( "forward.htm"  , "frame src=\"result"          ,"frame src=\"forward" )                                // 5.6.3.4 (2013/04/26) forward.htm にフレームを使うパターン(3ペイン)
055                ,new ChangeData( "reset.htm"    , "frame src=\"result"          ,"frame src=\"forward" )                                // 5.6.4.2 (2013/05/17) reset.htm で、フレームを使うパターン(3ペイン)
056                ,new ChangeData( "index.htm"    , "frame src=\"forward.htm"     ,"frame src=\"../common/dummy.html" )
057                ,new ChangeData( "index.htm"    , "frame src=\"entry.htm"       ,"frame src=\"../common/dummy.html" )   // 5.6.3.4 (2013/04/26) ENTRY系の特殊対応
058                ,new ChangeData( "indexRNW.htm" , "frame src=\"forward.htm"     ,"frame src=\"renew.htm" )
059                ,new ChangeData( "indexNW.htm"  , "frame src=\"query.htm"       ,"frame src=\"queryNW.htm" )
060                ,new ChangeData( "indexNW.htm"  , "frame src=\"entry.htm"       ,"frame src=\"../common/dummy.html" )   // 5.6.3.4 (2013/04/26) ENTRY系の特殊対応
061                ,new ChangeData( "entry"                , "<input[^>]*history.back[^>]*>"       ,"" )                                                   // 6.3.8.0 (2015/09/11) hiistory.back() のタグ削除 の対象を、entry を含むに変更
062                ,new ChangeData( null , "onSubmit=\"return oneClick\\(\\);\"","onSubmit=\"return false;\"" )            // 5.5.7.2 (2012/10/09) 変更は、すべて行う。
063                ,new ChangeData( null , "onSubmit=\"\""                                         ,"onSubmit=\"return false;\"" )                 // 5.6.3.4 (2013/04/26) onSubmit 引数のないケースへの対応
064                ,new ChangeData( null , "src=\"\\.\\./common/option/ajaxSubmit\\.js\\?v=[^\"]+\"","" )                          // 5.6.3.4 (2013/04/26) ajaxSubmit.js を削除
065                // 4.3.3.0 (2008/10/01) 戻るリンクの対応
066                ,new ChangeData( "queryNW.htm" ,"=\"http://.*jsp/+?"    ,"=\"../" )
067                ,new ChangeData( "queryNW.htm" ,"\"query.htm?"  ,"\"querNWy.htm?" )                                                                     // 6.4.2.1 (2016/02/05) クリアボタンのとび先
068                ,new ChangeData( "query.htm"   ,"renew\\('query.htm'"   ,"renew\\('queryNW.htm'" )                                      // 5.6.4.2 (2013/05/17) renew('query.htm' 変換
069                 // 漢字のボタンでは、後ろにショートカット文字が入る為、前方一致で挿入する。
070        //      ,new ChangeData( "forward.htm","value=\"追加","onClick=\"location.href='insert.htm'\" value=\"追加" )
071        //      ,new ChangeData( "forward.htm","value=\"複写","onClick=\"location.href='copy.htm'\"   value=\"複写" )
072        //      ,new ChangeData( "forward.htm","value=\"変更","onClick=\"location.href='modify.htm'\" value=\"変更" )
073        //      ,new ChangeData( "forward.htm","value=\"削除","onClick=\"location.href='delete.htm'\" value=\"削除" )
074        //      ,new ChangeData( "query.htm","index.htm\\?command=RENEW"        ,"indexRNW.htm?command=RENEW" )
075        //      ,new ChangeData( null           ,"index.htm\\?command=NEW"              ,"indexNW.htm?command=NEW" )
076                ,new IndexMatrixMenuData()                      // 4.3.3.0 (2008/10/01) マトリクスメニュー対応
077                ,new IndexChangeData()
078                ,new HrefChangeData()
079                ,new NoTranHrefChangeData()                     // 5.6.3.4 (2013/04/26) entry.htm に "noTransitionUrl" が存在するときの処理。
080                ,new FileDownloadChangeData()           // 5.6.4.2 (2013/05/17) fileDownload.htm 対応
081        };
082
083        /**
084         * デフォルトコンストラクター
085         *
086         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
087         */
088        public FileResponseTransform() { super(); }             // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
089
090        /**
091         * 変換を行います。
092         * 実際には、各内部クラスのメソッドで処理を行います。
093         *
094         * @og.rev 5.6.4.2 (2013/05/17) JFreeChart の画像ファイル(ChartTempフォルダ) のコピー
095         *
096         * @param       file    対象ファイル名
097         * @param       inStr   対象データ
098         *
099         * @return      変換後データ
100         */
101        public String replace( final String file,final String inStr ) {
102                String rtnStr = inStr;
103
104                // query 画面で、登録コマンドが発行された場合の特殊処理
105                if( file.indexOf( "query.htm" ) >= 0 && inStr.indexOf( "name=\"command\" value=\"登録" ) >= 0 ) {
106                        rtnStr = inStr.replace( "forward.jsp","entry.htm" );
107                }
108
109                // 5.6.4.2 (2013/05/17) JFreeChart の画像ファイル(ChartTempフォルダ) のコピー
110                if( inStr.indexOf( "/ChartTemp/" ) >= 0 ) {
111                        chartTempFileCopy( file,inStr );
112                }
113
114                for( int i=0; i<CHG_DATA.length; i++ ) {
115                        rtnStr = CHG_DATA[i].replace( file,rtnStr );
116                }
117                return rtnStr;
118        }
119
120        /**
121         * JFreeChart の画像ファイル(ChartTempフォルダ) のコピーを行います。
122         * これは、Tomcatを停止させずに、ChartTempフォルダ を人手てコピーする予定でしたが、
123         * 万一、停止させると、ファイルが自動削除されるため、自動コピー機能を入れておきます。
124         *
125         * ここの処理は、対象データ(inStr) の文字列変換ではなく、画像ファイルを見つけて、
126         * コピーするという処理を行います。非常に特殊な処理です。
127         *
128         * @og.rev 5.6.4.2 (2013/05/17) 新規追加
129         *
130         * @param       file    対象ファイル名
131         * @param       inStr   対象データ
132         */
133        private void chartTempFileCopy( final String file,final String inStr ) {
134                // 以下、決め打ちします。本当は saveDir や、ChartTemp をパラメータから取得すべき
135                // 大前提として、fromDir = filetemp/ChartTemp/
136                //               toDir   = filetemp/DIR/ChartTemp/
137
138                final int adrs = file.indexOf( "filetemp/DIR" );                        // jsp ファイルの出力先なので、DIR を含んでいます。
139                final File toDir   = new File( file.substring( 0,adrs ) + "filetemp/DIR/ChartTemp/" );
140
141                // コピー先ディレクトリが存在しなければ・・・
142                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
143                if( !toDir.exists() && !toDir.mkdirs() ) {
144                        System.err.println( toDir + " の ディレクトリ作成に失敗しました。" );
145                        return ;
146                }
147
148                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
149                final File fromDir = new File( file.substring( 0,adrs ) + "filetemp/ChartTemp/" );
150                // 画像ファイル名を求めます。
151                int st = inStr.indexOf( CHART_KEY ) ;
152                while( st >= 0 ) {
153                        final int ed = inStr.indexOf( ".png" ,st + KEY_LEN ) ;                  // 検索開始位置は、CHART_KEYの発見場所+文字数
154                        final String fname = inStr.substring( st + KEY_LEN , ed + 4 );  // + 4 は、".png" の分
155
156                        FileUtil.copy( new File( fromDir,fname ) , new File( toDir,fname ) );
157
158                        st = inStr.indexOf( CHART_KEY , ed + 4 ) ;
159                }
160        }
161
162        /**
163         * 個々の変換データを管理している、データクラス
164         * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
165         *
166         * ※ このクラスは継承されるため、final化しません。
167         */
168        private static class ChangeData {
169                private final String filename ;
170                private final String org ;
171                private final String rep ;
172
173                /**
174                 * デフォルトコンストラクター
175                 * サブクラス作成用に用意された、コンストラクタ。このクラス自身には不要。
176                 */
177                public ChangeData() {
178                        this( null,null,null );
179                }
180
181                /**
182                 * コンストラクター
183                 * 初期設定を行います。
184                 * 対象ファイル名は、処理を実行するかどうかの判定に使用します。
185                 * 指定の文字列が含まれているかどうかで判定します。
186                 * null の場合は、すべてのファイルを対象にします。
187                 *
188                 * @param       filename        対象ファイル名
189                 * @param       org     変換元データ
190                 * @param       rep     変換後データ
191                 */
192                public ChangeData( final String filename,final String org,final String rep ) {
193                        this.filename = filename;
194                        this.org = org;
195                        this.rep = rep;
196                }
197
198                /**
199                 * 実際に変換を行うメソッド
200                 * 内部的には、入力文字列.replaceAll( 変換元,変換後 ) メソッドを実行します。
201                 *
202                 * @param       file    対象ファイル名
203                 * @param       inStr   対象データ
204                 * @return      変換後データ
205                 */
206                public String replace( final String file,final String inStr ) {
207
208                        String rtnStr = inStr;
209                        if( filename == null || file.indexOf( filename ) >= 0 ) {
210                                rtnStr = inStr.replaceAll( org,rep );
211                        }
212                        return rtnStr;
213                }
214
215                /**
216                 * このオブジェクトの文字列表現
217                 * "ChangeData( " + filename + " , " + org + " , " + rep + " )" を返します。
218                 *
219                 * @return      文字列表現
220                 * @og.rtnNotNull
221                 */
222                @Override
223                public String toString() {
224                        return "ChangeData( " + filename + " , " + org + " , " + rep + " )" ;
225                }
226        }
227
228        /**
229         * マトリクスメニュー対応のデータ置き換えクラスです。
230         * jsp/ は、先に、../ に変換済みなので、その "../" からの検索条件で判断します。
231         * multiMenu と、matrixMenu は、標準は、menu フォルダですが、場合によっては、custom フォルダに存在する
232         * 場合があるため、menu/ は検索条件に含めません。
233         * ③と④は、変換処理は無いはずなので、ロジックは記述していません。
234         *
235         *   matrixMenu対応
236         *                 URI分離          URI分離           request取出
237         *              ① gamenId="jsp"  + index.jsp       + GAMENID=XXXX  ⇒ saveDir + "jsp/indexXXXX.htm"         Matrixメニューからの画面呼出し。
238         *              ② gamenId="jsp"  + result.jsp      + GAMENID=XXXX  ⇒ saveDir + "jsp/indexXXXX.htm"         画面QUERYのヘッダーメニュー
239         *              ③ gamenId="menu" + multiMenu.jsp   + group=YYYY    ⇒ saveDir + "menu/menuYYYY.htm"         通常メニューのグループ選択
240         *              ④ gamenId="menu" + matrixMenu.jsp  + group=YYYY    ⇒ saveDir + "menu/matrixMenuYYYY.htm"   Matrixメニューのグループ選択
241         *
242         * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
243         *
244         * @og.rev 4.3.3.0 (2008/10/01) Matrixメニュー対応
245         * @og.rev 5.5.2.5 (2012/05/21) TopMenuTag の ONELEVEL メニューリンク対応
246         * @og.rev 5.6.4.2 (2013/05/17) 正規表現修正、判定条件変更
247         * @og.rev 6.3.8.4 (2015/10/09) セーブフォルダを、URIではなく、画面IDから取得する。
248         * @og.rev 6.4.3.2 (2016/02/19) matrixMenu の、../画面ID/index.htm の場合の対応
249         */
250        private static final class IndexMatrixMenuData extends ChangeData {
251                /**
252                 * 実際に変換を行うメソッド。
253                 *
254                 * @param       file    対象ファイル名
255                 * @param       inStr   対象データ
256                 * @return      変換後データ
257                 */
258                @Override
259                public String replace( final String file,final String inStr ) {
260                        String rtnStr = inStr;
261
262                        // 5.6.4.2 (2013/05/17) 正規表現修正、判定条件変更
263                        // ① gamenId="jsp"  + index.jsp       + GAMENID=XXXX  ⇒ saveDir + "jsp/indexXXXX.htm"         Matrixメニューからの画面呼出し。
264                        // ④ gamenId="menu" + matrixMenu.jsp  + group=YYYY    ⇒ saveDir + "menu/matrixMenuYYYY.htm"   Matrixメニューのグループ選択
265                        if( file.indexOf( "matrixMenu" ) >= 0 ) {
266                                // 6.4.3.2 (2016/02/19) ../画面ID/index.htm の場合の対応
267                                // 6.4.3.2 (2016/02/19) 従来の../index.htm では、/ がひとつなので、注意。先頭の ".. にしておかないと、前方のタグまで食ってしまう。
268                                rtnStr = rtnStr.replaceAll( "\"../[^/]*[/]*index.htm\\?[^\"]*GAMENID=([^&\"]*)[^\"]*\"","\"../jsp/index$1.htm\"" );     // ①
269                                rtnStr = rtnStr.replaceAll( "matrixMenu.htm\\?[^\"]*group=([^&\"]*)[^\"]*\"","matrixMenu$1.htm\"" );    // ④
270                                rtnStr = rtnStr.replaceAll( "=\"../../../mr/jsp/","=\"../" );
271                        }
272                        // 6.3.8.4 (2015/10/09) セーブフォルダを、URIではなく、画面IDから取得する。
273                        // ① のケースの、jsp/indexXXXX.htm ファイルの内容(frame)のフォルダ名を画面IDに変更
274                        else if( file.indexOf( "jsp/index" ) >= 0 ) {
275                                rtnStr = rtnStr.replaceAll( "frame src=\"../[^\"]*GAMENID=([^&\"]*)[^\"]*\"","frame src=\"../$1/index.htm\"" );         // ①
276                        }
277                        // ② gamenId="jsp"  + result.jsp      + GAMENID=XXXX  ⇒ saveDir + "XXXX/index.htm"            画面QUERYのヘッダーメニュー
278                        else if( file.indexOf( "query" ) >= 0 ) {
279                                rtnStr = rtnStr.replaceAll( "../result.htm\\?[^\"]*GAMENID=([^&\"]*)[^\"]*\"","../jsp/index$1.htm\"" ); // ②
280                        }
281                        // ③ gamenId="menu" + multiMenu.jsp   + group=YYYY    ⇒ saveDir + "jsp/menuYYYY.htm"          通常メニューのグループ選択
282                        else if( file.indexOf( "multiMenu" ) >= 0 || file.indexOf( "menu" ) >= 0 || file.indexOf( "normalMenu" ) >= 0 ) {
283                                rtnStr = rtnStr.replaceAll( "multiMenu.htm\\?[^\"]*group=([^&\"]*)[^\"]*\"","menu$1.htm\"" );                   // ③
284                        }
285                        return rtnStr;
286                }
287
288                /**
289                 * このオブジェクトの文字列表現
290                 * "IndexMatrixMenuData()" を返します。
291                 *
292                 * @return      文字列表現
293                 * @og.rtnNotNull
294                 */
295                @Override
296                public String toString() {
297                        return "IndexMatrixMenuData()" ;
298                }
299        }
300
301        /**
302         * index.htm のコマンド単位のファイル名の置き換えクラスです。
303         * このクラスは、不変クラスのため、マルチスレッドでの同時使用に対して、安全です。
304         *
305         */
306        private static final class IndexChangeData extends ChangeData {
307                // <a href="aaaa/index.htm?command=RENEW&GAMENID=bbbb 形式とマッチし、index.htm 部分を前方参照します。
308                private static final Pattern PTN1 = Pattern.compile( "index.htm\\?[^\"]*command=(NEW|RENEW)" );
309
310                /**
311                 * 実際に変換を行うメソッド。
312                 *
313                 * @param       file    対象ファイル名
314                 * @param       inStr   対象データ
315                 * @return      変換後データ
316                 */
317                @Override
318                public String replace( final String file,final String inStr ) {
319                        String rtnStr = inStr;
320        //              if( file.indexOf( "query" ) >= 0 ) {            // query の場合
321                                final Matcher mch = PTN1.matcher( rtnStr );
322                                int adrs = 0;
323                                while( mch.find( adrs ) ) {
324                                        final int indx = mch.start() ;
325                                        final String cmd = mch.group(1);                // command=(NEW|RENEW) 部分の()内文字列
326                                        // index.htm 文字列部に、NW または RNW を追加し、indexNW.html を作成する。
327                                        if( "NEW".equalsIgnoreCase( cmd ) ) {
328                                                rtnStr = rtnStr.substring(0,indx+5) + "NW" + rtnStr.substring(indx+5) ;
329                                        }
330                                        else if( "RENEW".equalsIgnoreCase( cmd ) ) {
331                                                rtnStr = rtnStr.substring(0,indx+5) + "RNW" + rtnStr.substring(indx+5) ;
332                                        }
333                                        adrs = mch.end() ;
334                                        mch.reset( rtnStr );
335                                }
336        //              }
337                        return rtnStr;
338                }
339
340                /**
341                 * このオブジェクトの文字列表現
342                 * "IndexChangeData()" を返します。
343                 *
344                 * @return      文字列表現
345                 * @og.rtnNotNull
346                 */
347                @Override
348                public String toString() {
349                        return "IndexChangeData()" ;
350                }
351        }
352
353        /**
354         * コマンド転送先を、onClick="location.href=XXX" で指定するように、変換します。
355         * <input type="hidden" name="hX_複写(C)" value="copy.htm" /> を見つけ、
356         * 前方参照で、複写(C) と、copy.htm を取り出します。
357         * その後、<input name="command" value="複写(C)" という文字列をキーに、
358         * <input name="command" onClick="location.href='copy.htm'" value="複写(C)" という
359         * 文字列に置き換えます。
360         * これにより、ボタンを押したときに、ボタンごとに異なる画面に遷移します。
361         * 前提条件として、下記の項目を満たしておく必要がある。
362         *   ・form には、onSubmit="return false;" を記述し、フォームを無効化しておく。
363         *   ・input タグの type="submit" を、type="button" にしておく。(ボタンイベント)
364         *   ・query.htm 以外のファイルのみ適用。location.href では、フレームのtarget指定
365         *     まで行っていない。
366         *   ・上と同意で、query.htm の登録時処理は、別に行う。
367         *
368         * @og.rev 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
369         * @og.rev 5.6.4.2 (2013/05/17) 3ペイン、エントリなど、特殊な画面にフラグを付けます。(TARGET_CHANGE_SET)
370         * @og.rev 6.2.5.0 (2015/06/05) GMIS V6 対応(location.href時に、type="submit" を type="button" に変更)
371         */
372        private static final class HrefChangeData extends ChangeData {
373                private static final String PTN1 = "<input type=\"hidden\" name=\"hX_([^\"]*)\" value=\"([^\"]*.htm)" ;
374                private static final Pattern PTN_OBJ1 = Pattern.compile( PTN1 );                                                                                        // 6.4.1.1 (2016/01/16) ptnObj1 → PTN_OBJ1 refactoring
375
376                // 5.5.7.2 (2012/10/09) 定数名の変更
377                private static final String ORG  = "<input name=\"command\"" ;
378                private static final String SELF = "<input name=\"command\" onClick=\"location.href='" ;
379                private static final String PRNT = "<input name=\"command\" onClick=\"parent.location.href='" ;
380                private static final String TOP  = "<input name=\"command\" onClick=\"top.location.href='" ;
381
382                // 5.5.7.2 (2012/10/09) formのtargetを取得。location.href に利用する。
383                private static final String PTN2 = "<form .*target=\"([^\"]*)\"" ;
384                private static final Pattern PTN_OBJ2 = Pattern.compile( PTN2 );                                                                                        // 6.4.1.1 (2016/01/16) ptnObj2 → PTN_OBJ2 refactoring
385
386                // 6.2.5.0 (2015/06/05) GMIS V6 対応(location.href時に、type="submit" を type="button" に変更)
387                private static final String LOC_HREF  = "location.href" ;
388                private static final String TP_SUBMIT = "type=\"submit\"" ;
389                private static final String TP_BUTTON = "type=\"button\"" ;
390
391                /**
392                 * コマンド転送先を、onClick="location.href=XXX" で指定するように、変換します。
393                 * <input type="hidden" name="hX_複写(C)" value="copy.htm" /> を見つけ、
394                 * 前方参照で、複写(C) と、copy.htm を取り出します。
395                 * その後、<input name="command" value="複写(C)" という文字列をキーに、
396                 * <input name="command" onClick="location.href='copy.htm'" value="複写(C)" という
397                 * 文字列に置き換えます。
398                 *
399                 * @og.rev 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
400                 * @og.rev 5.5.7.2 (2012/10/09) 定数名の変更。formのtargetを加味した、location.href を作成する。
401                 * @og.rev 5.6.4.2 (2013/05/17) 3ペイン、エントリなど、特殊な画面にフラグを付けます。(TARGET_CHANGE_SET)
402                 * @og.rev 6.2.5.0 (2015/06/05) query*** 以外
403                 * @og.rev 6.3.8.0 (2015/09/11) idxEnd = -1 の場合、無限ループになる。(javadoc上では -1 のハズ)
404                 *
405                 * @param       file    対象ファイル名
406                 * @param       inStr   対象データ
407                 * @return      変換後データ
408                 */
409                @Override
410                public String replace( final String file,final String inStr ) {
411                        String rtnStr = inStr;
412                        // 6.2.5.0 (2015/06/05) query.htm , queryNW.htm , query1.htm 以外
413                        if( file.indexOf( "query" ) < 0 ) {                     // 6.2.5.0 (2015/06/05) query*** 以外
414                                // 5.5.7.2 (2012/10/09) formのtargetを加味した、location.href を作成する。
415                                final Matcher mch2 = PTN_OBJ2.matcher( rtnStr );
416                                String ptnHref = SELF;                  // 標準は、location.href
417                                if( mch2.find() ) {
418                                        // 5.6.4.2 (2013/05/17) 3ペイン、エントリなど、特殊な画面にフラグを付けます。(TARGET_CHANGE_SET)
419                                        final int indx = file.lastIndexOf( '/' );
420                                        final String fileKey = file.substring( 0,indx );
421
422                                        final String frmTgt = mch2.group(1);
423                                        if( "CONTENTS".equals( frmTgt ) )  { ptnHref = PRNT; }
424                                        else if( "_top".equals( frmTgt ) ) { ptnHref = TOP;  }
425                                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
426                                        else if( "RESULT".equals( frmTgt ) || frmTgt == null ) {
427                                                // 5.6.4.2 (2013/05/17) ある画面で、特殊なターゲット(INPUT,BUTTOMなど)を使用している場合のチェック
428                                                if( TARGET_CHANGE_SET.contains( fileKey ) ) {
429                                                        ptnHref = PRNT ;
430                                                }
431                                        }
432                                        else {
433                                                ptnHref = "<input name=\"command\" onClick=\"parent." + frmTgt + ".location.href='" ;
434                                                // 5.6.4.2 (2013/05/17) ある画面で、特殊なターゲット(INPUT,BUTTOMなど)を使用している場合に記憶
435                                                TARGET_CHANGE_SET.add( fileKey );               // 別のファイルを処理するときに参照する。
436                                        }
437                                }
438
439                                final Matcher mch = PTN_OBJ1.matcher( rtnStr );
440                                int adrs = 0;
441                                while( mch.find( adrs ) ) {
442                                        final String cmd = mch.group(1);
443                                        if( !cmd.endsWith( "CMD" ) ) {
444                                                final String val = mch.group(2);
445                                                final String str1 = ORG + " value=\"" + cmd ;
446                                                String str2 ;
447
448                                                if( val != null && val.startsWith( "../" ) ) {
449                                                        str2 = PRNT + val + "'\" value=\"" + cmd ;
450                                                }
451                                                // 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
452                                                else if( val != null && val.startsWith( "update" ) ) {
453                                                        str2 = ptnHref + cmd + ".htm'\" value=\"" + cmd ;
454                                                }
455                                                else {
456                                                        str2 = ptnHref + val + "'\" value=\"" + cmd ;
457                                                }
458                                                rtnStr = rtnStr.replace( str1,str2 );
459                                        }
460                                        adrs = mch.end();
461                                        mch.reset( rtnStr );
462                                }
463
464                                // 6.2.5.0 (2015/06/05) GMIS V6 対応(location.href時に、type="submit" を type="button" に変更)
465                                // 正規表現の置き換え箇所が、難しそうなので、もう一度初めから処理します。
466                                // location.href① … type="submit"② … />③ の関係チェック
467                                final StringBuilder buf = new StringBuilder( rtnStr );
468
469                                int idxHref = buf.indexOf( LOC_HREF );                                          // ①
470                                while( idxHref >= 0 ) {
471                                        final int idxType = buf.indexOf( TP_SUBMIT , idxHref ); // ②
472                                        final int idxEnd  = buf.indexOf( "/>"      , idxHref ); // ③
473                                        if( idxType >= 0 && idxEnd >= 0 && idxType < idxEnd ) {
474                                                buf.replace( idxType , idxType + TP_SUBMIT.length() , TP_BUTTON );      // 範囲置換
475                                        }
476                                        // 6.3.8.0 (2015/09/11) idxEnd = -1 の場合、無限ループになる。(javadoc上では -1 のハズ)
477                                        idxHref = buf.indexOf( LOC_HREF , Math.max( idxEnd , idxHref + LOC_HREF.length() ) );
478                                }
479                                rtnStr = buf.toString();
480                        }
481                        return rtnStr;
482                }
483
484                /**
485                 * このオブジェクトの文字列表現
486                 * "HrefChangeData()" を返します。
487                 *
488                 * @return      文字列表現
489                 * @og.rtnNotNull
490                 */
491                @Override
492                public String toString() {
493                        return "HrefChangeData()" ;
494                }
495        }
496
497        /**
498         * 雛形自動作成 で、useAjaxSubmit="true" の対策
499         *
500         * update.jsp で、useAjaxSubmit="true" の場合、entry.htm は、update.jsp の 
501         * JavaScriptでforward されるため、雛形には、HTMLの結果は出力されません。
502         * (result.jsp に出力されます。)
503         * そこで、雛形作成時には、entry.htm にJavaScriptを入れて、forward させます。
504         * JavaScript の挿入位置は、 BODYの終了タグがあれば、その直前に、なければ、最後に追加します。
505         *
506         * ※ 6.2.5.0 (2015/06/05)
507         *    本当は、ForwardTag#doEndTag() の noTransitionUrl を出力している箇所に入れれば
508         *    一番きれいだが、各種各画面で使用しているタグなので、あまり変更したくないのと、
509         *    style 属性で設定すると、画面が動作しなくなったため、何か問題が起こりそうなので、
510         *    紙芝居作成時のみ対応することとします。
511         *    非表示(display:none;)は、<div id="noTransitionUrl"> の直前にします。
512         *
513         * @og.rev 5.6.3.4 (2013/04/26) entry.htm に "noTransitionUrl" が存在するときの処理。
514         * @og.rev 6.2.5.0 (2015/06/05) GMIS V6 対応(noTransitionUrl,noTransitionTargetを非表示にする)
515         */
516        private static final class NoTranHrefChangeData extends ChangeData {
517                // 6.2.5.0 (2015/06/05) GMIS V6 対応(noTransitionUrl,noTransitionTargetを非表示にする)
518                private static final String DIV_TAG      = "<div id=\"noTransitionUrl\">" ;
519                private static final String DISPLAY_NONE = "<style type=\"text/css\">#noTransitionUrl,#noTransitionTarget{display:none;}</style>";
520
521                private static final String BODY_END   = "</body>" ;
522                private static final String APPEND_JS  = "<script type=\"text/javascript\" src=\"../common/option/noTranHref.js\" ><!-- --></script>" ;
523
524                /**
525                 * 実際に変換を行うメソッド。
526                 *
527                 * @param       file    対象ファイル名
528                 * @param       inStr   対象データ
529                 * @return      変換後データ
530                 */
531                @Override
532                public String replace( final String file,final String inStr ) {
533                        String rtnStr = inStr;
534                        // entry.jsp で、かつ noTransitionUrl 文字列を含む場合のみ
535                        if( file.indexOf( "entry" ) >= 0 && inStr.indexOf( "noTransitionUrl" ) >= 0 ) {
536                                // 6.2.5.0 (2015/06/05) GMIS V6 対応
537                                final StringBuilder buf = new StringBuilder( rtnStr );
538                                final int divAd = buf.indexOf( DIV_TAG );
539                                if( divAd >= 0 ) { buf.insert( divAd,DISPLAY_NONE ); }
540
541                                final int adrs = buf.indexOf( BODY_END );
542                                if( adrs >= 0 ) { buf.insert( adrs,APPEND_JS ); }       // </body> タグが存在した場合は、その直前に挿入する。
543                                else            { buf.append( APPEND_JS ); }            // 存在しない場合は、最後に挿入する。
544
545                                rtnStr = buf.toString();
546                        }
547                        return rtnStr;
548                }
549
550                /**
551                 * このオブジェクトの文字列表現
552                 * "NoTranHrefChangeData()" を返します。
553                 *
554                 * @return      文字列表現
555                 * @og.rtnNotNull
556                 */
557                @Override
558                public String toString() {
559                        return "NoTranHrefChangeData()" ;
560                }
561        }
562
563        /**
564         * 雛形自動作成 で、fileDownload の日本語名対応
565         *
566         * 標準的な、fileDownload 処理では、../common/fileDownload.jsp?・・・・GAMENID=XXXX&filename=YYYY" と
567         * なっており、filename 部分は、日本語にも対応できるように urlEncode されています。
568         * これを、元に戻さないとうまくダウンロードできませんでした。
569         *
570         * ※ 参考情報
571         *  1.urlEncode のままで、ファイル名を取得する場合は、下記の標準系で対応可能です。
572         *              ,new ChangeData( null ,"../common/fileDownload.htm\\?[^\"]*filename=([^&\"]*)[^\"]*\"","$1\"" )
573         *  2.ファイル名を、fileDownload.xls 固定にする場合は、下記の標準系で対応可能です。
574         *              ,new ChangeData( null ,"../common/fileDownload.htm\\?[^\"]*\"","fileDownload.xls\"" )
575         *
576         * @og.rev 5.6.4.2 (2013/05/17) 新規追加
577         */
578        private static final class FileDownloadChangeData extends ChangeData {
579                private static final String PTN1 = "../common/fileDownload.htm\\?[^\"]*filename=([^&\"]*)[^\"]*\"" ;
580                private static final Pattern PTN_OBJ1 = Pattern.compile( PTN1 );                                                                                        // 6.4.1.1 (2016/01/16) ptnObj1 → PTN_OBJ1 refactoring
581
582                /**
583                 * 実際に変換を行うメソッド。
584                 *
585                 * @param       file    対象ファイル名
586                 * @param       inStr   対象データ
587                 * @return      変換後データ
588                 */
589                @Override
590                public String replace( final String file,final String inStr ) {
591                        String rtnStr = inStr;
592                        // entry.jsp で、かつ noTransitionUrl 文字列を含む場合のみ
593                        if( rtnStr.indexOf( "../common/fileDownload.htm" ) >= 0 ) {
594                                final Matcher mch = PTN_OBJ1.matcher( rtnStr );
595                                int adrs = 0;
596                                while( mch.find( adrs ) ) {
597                                        String fname = mch.group(1);
598                                        fname = StringUtil.urlDecode( fname );          // デコードしています。
599
600                                        final int indx = mch.start() ;
601                                        adrs = mch.end();
602                                        rtnStr = rtnStr.substring( 0,indx ) + fname + "\"" + rtnStr.substring( adrs ) ;
603
604                                        mch.reset( rtnStr );
605                                }
606                        }
607                        return rtnStr;
608                }
609
610                /**
611                 * このオブジェクトの文字列表現
612                 * "FileDownloadChangeData()" を返します。
613                 *
614                 * @return      文字列表現
615                 * @og.rtnNotNull
616                 */
617                @Override
618                public String toString() {
619                        return "FileDownloadChangeData()" ;
620                }
621        }
622}