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