    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.taglib;
    017
    018import org.opengion.hayabusa.common.HybsSystem;
    019import org.opengion.hayabusa.common.HybsSystemException;
    020import org.opengion.hayabusa.db.DBTableModel;
    021import org.opengion.fukurou.util.ErrorMessage;
    022import org.opengion.fukurou.util.XHTMLTag;
    023import org.opengion.fukurou.util.StringUtil;
    024import org.opengion.fukurou.util.ToString;                                                      // 6.1.1.0 (2015/01/17)
    025
    026import static org.opengion.fukurou.util.StringUtil.nval ;
    027
    028import jakarta.servlet.http.HttpServletRequest;
    029import jakarta.servlet.http.HttpServletResponse;
    030import jakarta.servlet.http.HttpSession;
    031import jakarta.servlet.ServletException;
    032import java.io.IOException;
    033import java.util.Enumeration;
    034import java.util.Map;
    035import java.util.HashMap;
    036import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
    037import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
    038
    039/**
    040 * submitタグを用いてページ転送するタグです(forward.jsp で使用)。
    041 *
    042 * 通常、forward は、ページ内転送(サーバー内部での転送)のため、別画面への遷移には
    043 * 使用できません。これは、別画面では、index.jsp 等でフレーム分割する際の基準フォルダは、
    044 * forward の場合、クライアントは理解していないため、もう一度先のフォルダに対する
    045 * フレーム分割を行おうとするためです。
    046 * (よく、index.jsp の frame タグに、src="../XXXX/query.jsp" などと、自分自身のフォルダ名を
    047 * 記述したページを見かけますが、これは、フォルダをまたがる転送に、forward を使用する
    048 * 為の悪い対策です。)
    049 * 実際は、forward ではなく、redirect を使うべきです。redirect は、指定のアドレス要求を、
    050 * 一旦クライアントに投げてそこから再度要求しなおしてもらう方式のため、このようにフォルダを
    051 * またがる転送も正常に処理できます。
    052 * この、commonForward タグでは、画面遷移の条件に応じて、forward か redirect の自動
    053 * 判定を行い、適切に処理しています。
    054 * 判定条件は、拡張子や、選択件数などを加味して以下の判定を順次テストします。
    055 *
    056 *   FORWARD :
    057 *       アドレスが、 null(自分自身) か、.jsp を含み、"/" が入っていない場合
    058 *   REDIRECT:
    059 *       アドレスが、.jsp を含まないか、
    060 *       それ以外(.jsp を含み、"/" も含む)で、選択数が１件のみの場合
    061 *       もしくはuseRedirectCheck="false"の場合
    062 *   COUNT_0 :
    063 *       それ以外で、選択数が０件の場合
    064 *   COUNT_N :
    065 *       それ以外で、選択数が１件以上の場合、または、その他。
    066 *
    067 * ここで、COUNT_0 の場合は、未選択エラー、COUNT_N は、複数選択エラーを自動的に返します。
    068 *
    069 * @og.formSample
    070 * ●形式：&lt;og:commonForward /&gt;
    071 * ●body：なし
    072 *
    073 * ●Tag定義：
    074 *   &lt;og:commonForward
    075 *       dbkeys             【TAG】DBキーをCSV形式でセットします
    076 *       tableId            【TAG】(通常は使いません)sessionから所得する DBTableModelオブジェクトの ID
    077 *       useRedirectCheck   【TAG】非選択状態の場合にforwardを許可するかどうか[true/false]を指定します(初期値:true)
    078 *       useRedirectHidden  【TAG】redirectの処理時に、hiddenパラメータを転送するかどうか[true:する/false:しない]指定します(初期値:false)。
    079 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    080 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
    081 *   /&gt;
    082 *
    083 * ●使用例
    084 *     フォワードキャッシュによりページ転送します。
    085 *     &lt;og:commonForward dbkeys="{&#064;dbkeys}" /&gt;
    086 *
    087 * @og.group 画面制御
    088 *
    089 * @version  4.0
    090 * @author       Masaharu Endo
    091 * @since    JDK5.0,
    092 */
    093public class CommonForwardTag extends CommonTagSupport {
    094        /** このプログラムのVERSION文字列を設定します。   {@value} */
    095        private static final String VERSION = "7.0.7.0 (2019/12/13)" ;
    096        private static final long serialVersionUID = 707020191213L ;
    097
    098        // 3.5.5.3 (2004/04/09) 共通アドレスで指定することで、クライアントキャッシュを有効利用する。
    099        private static final String DUMMY_HTML  = "/" + HybsSystem.getContextName() + "/jsp/common/dummy.html";
    100
    101        // 3.8.5.1 (2006/04/28) dbkeys が null の場合に全件取得するかどうかを COMMON_FORWARD_DBKEYS_NULL_ALL で指定します。
    102
    103        private static final int FORWARD  = 0;
    104        private static final int REDIRECT = 1;
    105        private static final int COUNT_0  = 2;
    106        private static final int COUNT_N  = 3;
    107
    108        // 3.5.5.2 (2004/04/02) 選択行が、１行のみか、そうでないか
    109        private int rowCount = -1;
    110
    111        private int     rowNo    = -1;
    112
    113        // 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
    114        private String  dbkeys          ;
    115
    116        // 3.5.5.8 (2004/05/20) submitタグの keys,vals を扱う 仮想リクエストMap
    117        private String  tableIdTemp     ;
    118        private String  dbkeysTemp      ;
    119        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
    120        private final transient ConcurrentMap<String,String> submitRequestMap = new ConcurrentHashMap<>();
    121        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
    122        private final transient ConcurrentMap<String,String> submitTableMap       = new ConcurrentHashMap<>();
    123
    124        // 4.0.0.0 (2007/11/09) 非選択状態でのforwardのための属性追加
    125        private boolean isRedirectCheck         = true;
    126
    127        // 6.2.4.0 (2015/05/15) redirectの処理時に、hiddenパラメータを転送するかどうか[true:する/false:しない]
    128        // 互換性の関係で、初期値は、false にします。
    129        private boolean isRedirectHidden        ;
    130
    131        // 7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
    132        private boolean         useSLabel       ;
    133
    134        /**
    135         * デフォルトコンストラクター
    136         *
    137         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    138         */
    139        public CommonForwardTag() { super(); }          // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    140
    141        /**
    142         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    143         *
    144         * @og.rev 3.3.1.1 (2003/07/03) ＵＲＬにリクエスト情報をＵＲＬエンコードして追加します。
    145         * @og.rev 3.5.5.2 (2004/04/02) フォルダ外転送時は、１行以外選択は、エラーとします。
    146         * @og.rev 3.5.5.3 (2004/04/09) デバッグ時は、転送しないようにします。
    147         * @og.rev 3.8.0.4 (2005/08/08) requestUrlEncode 廃止
    148         * @og.rev 3.8.0.8 (2005/10/03) dbkeys が null の場合に全件取得していた処理を復活します。
    149         * @og.rev 4.0.0.0 (2007/11/09) 非選択状態からの遷移を許可するフラグを追加(11/12に振り分け処理をselectResponseMethodに移動)
    150         * @og.rev 5.0.0.2 (2009/09/15) XSS対応⇒チェックする
    151         * @og.rev 5.3.6.0 (2011/06/01) エラーメッセージ変更(URLの振り分け処理時に...のメッセージは出力しない)
    152         * @og.rev 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
    153         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
    154         *
    155         * @return      後続処理の指示
    156         */
    157        @Override
    158        public int doEndTag() {
    159                debugPrint();           // 4.0.0 (2005/02/28)
    160
    161                // useXssCheck( false ); // 5.0.0.2 (2009/09/15)
    162
    163                final HttpServletRequest request  = ((HttpServletRequest)getRequest());
    164                final HttpSession session = pageContext.getSession();
    165                String page = getForwardURI( request, session );
    166
    167                // 3.5.5.8 (2004/05/20) 内部仮想リクエストMap より値を取得
    168                dbkeys = nval( getSubmitRequestParameter( dbkeysTemp ),dbkeys );
    169
    170                final HttpServletResponse response = (HttpServletResponse)pageContext.getResponse();
    171
    172                String errMsgKey = null;
    173                try {
    174
    175                        // 3.8.0.8 (2005/10/03) dbkeys が null の場合に全件取得していた処理を復活します。
    176                        // 6.2.4.0 (2015/05/15) dbkeys != null の場合しか、[]付のパラメータを処理できなかった。
    177                        if( rowCount == 1 && ( dbkeys != null || !submitTableMap.isEmpty() ) ) {                // 6.3.9.0 (2015/11/06) size() == 0 判定は、isEmpty() に置き換え可能
    178                                page = XHTMLTag.addUrlEncode( page,getTableUrlData() );
    179                        }
    180
    181                        final String url = response.encodeRedirectURL( page );
    182                        // 3.8.0.8 (2005/10/03) GET時の URL の長さ制限チェック(最大文字数は 2,083 文字)
    183                        if( url != null && url.length() >= HybsSystem.MAX_GET_LENGTH ) {
    184                                final String errMsg = "GET時の URL の長さは,最大2,083 文字です。"
    185                                                        + " URL.length=" + url.length() + " , MAX_LENGTH=" + HybsSystem.MAX_GET_LENGTH ;
    186                                throw new HybsSystemException( errMsg );
    187                        }
    188
    189                        // 6.4.9.1 (2016/08/05) Avoid declaring a variable if it is unreferenced before a possible exit point.
    190                        final int flag = selectResponseMethod( page );
    191                        switch( flag ) {
    192                                case FORWARD:   if( isDebug() ) { jspPrint( "FORWARD URL = [" + url + "]" ); }
    193                                                                else { pageContext.forward( url ); }
    194                                                                break;
    195                                case REDIRECT:  // url = requestUrlEncode( url );               // 3.8.0.4 (2005/08/08)
    196                                                                if( isDebug() ) { jspPrint( "REDIRECT URL = [" + url + "]" ); }
    197                                                                else { response.sendRedirect( url ); }
    198                                                                break;
    199                                case COUNT_0:
    200                                                                errMsgKey = "ERR0028" ;  // 選択されていません。もう一度、選択しなおして下さい。
    201                                                                break;
    202                                default: errMsgKey = "ERR0029" ;        // 複数選択されました。１件のみ選択しなおして下さい。
    203                                                                break;
    204                        }
    205                }
    206                // 7.2.9.5 (2020/11/28) PMD:'catch' branch identical to 'IOException' branch
    207                catch( final IOException | ServletException ex ) {
    208                        // 5.3.6.0 (2011/06/01) エラーメッセージ表示変更
    209                        final String errMsg = ex.getMessage();                                  // 5.1.8.0 (2010/07/01) errMsg 修正
    210                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
    211                }
    212//              } catch( final IOException ex ) {
    213//                      // 5.3.6.0 (2011/06/01) エラーメッセージ表示変更
    214//                      final String errMsg = ex.getMessage();                                  // 5.1.8.0 (2010/07/01) errMsg 修正
    215//                      throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
    216//              } catch( final ServletException ex ) {
    217//                      // 5.3.6.0 (2011/06/01) エラーメッセージ表示変更
    218//                      final String errMsg = ex.getMessage();                                  // 5.1.8.0 (2010/07/01) errMsg 修正
    219//                      throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
    220//              }
    221
    222                // 3.5.5.2 (2004/04/02) フォルダ外転送時は、１行以外選択は、エラーとします。
    223                if( errMsgKey != null ) {
    224                        final ErrorMessage errMsg = new ErrorMessage( "Row Count Error Maximal Error!" );
    225                        errMsg.addMessage( 0,ErrorMessage.NG,errMsgKey );
    226
    227//                      jspPrint( TaglibUtil.makeHTMLErrorTable( errMsg,getResource() ) );
    228                        jspPrint( TaglibUtil.makeHTMLErrorTable( errMsg,getResource(),useSLabel ) );            // 7.0.7.0 (2019/12/13)
    229                }
    230
    231                return SKIP_PAGE ;              // ページの残りの処理を行わない。
    232        }
    233
    234        /**
    235         * タグリブオブジェクトをリリースします。
    236         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    237         *
    238         * @og.rev 3.5.5.2 (2004/04/02) 新規追加(rowCount,useTableData)
    239         * @og.rev 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
    240         * @og.rev 3.8.5.1 (2006/04/28) URLに連結するDBTableModelのカラムを[カラム]形式で指定します。
    241         * @og.rev 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
    242         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
    243         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
    244         */
    245        @Override
    246        protected void release2() {
    247                super.release2();
    248                rowCount     = -1;
    249                rowNo            = -1;
    250                dbkeys           = null;                        // 3.5.5.5 (2004/04/23)
    251                submitRequestMap.clear();               // 6.4.3.3 (2016/03/04)
    252                submitTableMap.clear();                 // 6.4.3.3 (2016/03/04)
    253                tableIdTemp                     = null;         // 3.5.5.8 (2004/05/20)
    254                dbkeysTemp                      = null;         // 3.5.5.8 (2004/05/20)
    255                isRedirectCheck         = true;         // 4.0.0.0 (2007/11/12)
    256                isRedirectHidden        = false;        // 6.2.4.0 (2015/05/15)
    257                useSLabel                       = false;        // 7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
    258        }
    259
    260        /**
    261         * フォワード先URIを取得します。
    262         *
    263         * @og.rev 3.1.2.0 (2003/04/07) ソースコード中の固定値を、定義ファイルを使用するように変更する。
    264         * @og.rev 3.1.4.1 (2003/04/21) request.getQueryString() をaddUrlEncodeしている箇所を削除。
    265         * @og.rev 3.1.7.0 (2003/05/02) コマンド RENEW で、forward した場合に、result.jsp に遷移するように修正。
    266         * @og.rev 3.1.7.0 (2003/05/02) 画面ＩＤのセットで、自画面を、BACK_GAMENID 、飛び先を、GAMENID にする。
    267         * @og.rev 3.1.8.0 (2003/05/16) SubmitData クラスを利用するように変更。
    268         * @og.rev 3.3.1.1 (2003/07/03) ForwardManager クラスの廃止。飛び先のキャッシュを廃止します。
    269         * @og.rev 3.5.5.2 (2004/04/02) 選択行の件数を設定しておきます。
    270         * @og.rev 3.5.5.3 (2004/04/09) dummy.html を static final で絶対パス指定します。
    271         * @og.rev 3.5.5.4 (2004/04/15) メソッド内で使用していない、gamenId,jspID 変数を削除します。
    272         * @og.rev 3.5.5.5 (2004/04/23) 余計なボタン関連情報を転送しない為に、キーを変更します。
    273         * @og.rev 3.5.5.5 (2004/04/23) SubmitTag の keys,vals 属性で指定した値のみ、転送します。
    274         * @og.rev 3.5.5.8 (2004/05/20) SubmitTag の keys,vals 属性で指定した値を、内部仮想リクエスト Mapにセットします。
    275         * @og.rev 3.7.0.1 (2005/01/31) リクエスト変数に選択された件数を追加
    276         * @og.rev 3.7.0.3 (2005/03/01) 指定の行番号まで画面をスクロールさせる機能を追加。
    277         * @og.rev 3.8.0.8 (2005/10/03) BACK_GAMENID があれば BACK_ROW を追加する。
    278         * @og.rev 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
    279         * @og.rev 5.1.8.0 (2010/07/01) VIEWの場合も、直前のJSPに遷移する。
    280         * @og.rev 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
    281         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
    282         * @og.rev 6.4.5.2 (2016/05/06) UPLOADから呼ばれた場合は、RESET  は、command="RENEW" , displayMsg="　" にします。
    283         *
    284         * @param       request HttpServletRequestオブジェクト
    285         * @param       session HttpSessionオブジェクト
    286         *
    287         * @return      フォワード先URI
    288         * @og.rtnNotNull
    289         */
    290        private String getForwardURI( final HttpServletRequest request,
    291                                                                  final HttpSession        session ) {
    292
    293                final String[] rows   = request.getParameterValues( HybsSystem.ROW_SEL_KEY );
    294
    295                // 4.0.0 (2007/05/16) query.jsp で複数command 時の処理修正
    296                String   cmd  = request.getParameter( "command" );
    297                final String[] cmds = request.getParameterValues( "command" );
    298                if( cmds != null && cmds.length > 1 ) {
    299                        for( int i=0; i<cmds.length; i++ ) {
    300                                if( ! "NEW".equals( cmds[i] ) ) {
    301                                        cmd = cmds[i]; break;   // NEW でない、最初の一つ
    302                                }
    303                        }
    304                        // すべてが NEW の場合は、単体(getParameter) が NEW なので素通りでよい。
    305                }
    306
    307                // 3.5.5.2 (2004/04/02) 選択行の件数
    308                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    309                if( rows == null ) {
    310                        rowCount = 0;
    311                 }
    312                else {
    313                        rowCount = rows.length;
    314                        rowNo = Integer.parseInt( rows[0] );
    315                }
    316                // 3.7.0.1 (2005/01/31) リクエスト変数に選択された件数を追加
    317                setRequestAttribute( "ROW_COUNT",String.valueOf( rowCount ) );
    318
    319                if( cmd == null || cmd.isEmpty() ) { cmd = "INIT"; }
    320                final String forwardPage;
    321
    322                final String backPage = (String)session.getAttribute( HybsSystem.FORWARD_PAGE_KEY );
    323                String command  = cmd;          // 3.5.5.5 (2004/04/23)
    324                int    pagePlus = 0;
    325
    326                // コマンドパラメータにより振分け
    327                // 5.1.8.0 (2010/07/01) VIEWの場合も、直前のJSPに遷移する。
    328                if( "FIRST,PREV,NEXT,LAST,VIEW".indexOf( cmd ) >= 0 ) {
    329                        forwardPage = backPage;
    330                } else if( "NEW,RENEW".indexOf( cmd ) >= 0 ) {
    331                        // 初期値
    332                        forwardPage = "result.jsp";
    333                } else if( "INIT".equals( cmd ) ) {
    334                        forwardPage = DUMMY_HTML;                       // 3.5.5.3 (2004/04/09)
    335                } else {
    336                        // 共有オブジェクト検索
    337
    338                        // リンク元コマンド名取得
    339                        // 3.5.5.5 (2004/04/23) 余計なボタン関連情報を転送しない為に、キーを変更します。
    340                        command = request.getParameter( HybsSystem.NO_XFER_KEY + cmd + "CMD" );
    341
    342                        // 3.7.0.3 (2005/03/01) 指定の行番号まで画面をスクロールさせる機能を追加。
    343                        if( rows != null && "ENTRY".equals( command ) ) {
    344                                setRequestCacheData( "SEL_ROW",String.valueOf( rowNo ) );
    345                        }
    346
    347                        if( "RESET".equals( command ) ) { // RESET 時
    348                                forwardPage = "result.jsp";
    349
    350                                // 6.4.5.2 (2016/05/06) UPLOADから呼ばれた場合は、RESET  は、command="RENEW" , displayMsg="　" にします。
    351                                if( Boolean.parseBoolean( request.getParameter( HybsSystem.USE_UPLOAD_KEY ) ) ) {
    352                                        command = "RENEW";
    353                                        setRequestAttribute( "displayMsg","　" );                // RESETなので、メッセージは出さない。
    354                                        setRequestCacheData( "SEL_ROW"   ,null );               // RENEWで戻るため、以前に行選択があったら、そこに戻ってしまうため、クリアしておく。
    355                                }
    356                        }
    357                        else {
    358                                // リンク先取得
    359                                forwardPage = request.getParameter( HybsSystem.NO_XFER_KEY + cmd );
    360                                // INSERTとCOPYの場合のみ
    361                                if( "INSERT".equals( command ) || "COPY".equals( command ) ) {
    362                                        if( rows != null ) { pagePlus = rows.length; }
    363                                }
    364                        }
    365                }
    366
    367                if( ! forwardPage.equals( backPage ) ) {
    368                        session.setAttribute( HybsSystem.REVIEW_PAGE_KEY , backPage );
    369                }
    370                session.setAttribute( HybsSystem.FORWARD_PAGE_KEY, forwardPage );
    371
    372                // 3.5.5.5 (2004/04/23) SubmitTag の keys,vals 属性で指定した値のみ、転送します。
    373                // 6.0.2.5 (2014/10/31) char を append する。
    374                final StringBuilder strURL = new StringBuilder( BUFFER_MIDDLE )
    375                        .append( '&' ).append( "command"  ).append( '=' ).append( command )
    376                        .append( '&' ).append( "pagePlus" ).append( '=' ).append( String.valueOf( pagePlus ) );
    377
    378                final String btnKey = HybsSystem.NO_XFER_KEY + cmd + "KEY_" ;
    379                final int    keylen = btnKey.length() ;
    380
    381                final Enumeration<?> enm = getParameterNames();         // 4.3.3.6 (2008/11/15) Generics警告対応
    382
    383                // 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
    384                final Map<String,String> hiddenMap = new HashMap<>();
    385                boolean isGamenIdFlag = false;
    386
    387                while( enm.hasMoreElements() ) {
    388                        final String key = (String)( enm.nextElement() );
    389                        // 3.5.5.5 (2004/04/23) 余計な情報を転送しない様に、キーを選別します。
    390                        if( key != null && key.startsWith( btnKey ) ) {
    391                                // 3.5.5.8 (2004/05/20) 内部の仮想リクエスト Map に設定します。
    392                                final String kk = key.substring( keylen ) ;
    393                                final String vv = nval( getRequestValue( key ) , "" );                  // 6.4.3.1 (2016/02/12) ConcurrentHashMap の条件は、key,val ともに、not null
    394                                // 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
    395                                if( vv.length() > 2 && vv.charAt(0) == '[' && vv.charAt(vv.length()-1) == ']' ) {
    396                                        submitTableMap.put( kk,vv.substring( 1,vv.length()-1 ));
    397                                }
    398        //                      else if( "dbkeys".equals( kk ) ) {
    399        //                              submitRequestMap.put( kk,vv );
    400        //                      }
    401                                else {
    402                                        submitRequestMap.put( kk,vv );
    403                                        strURL.append( '&' )                                    // 6.0.2.5 (2014/10/31) char を append する。
    404                                                .append( kk ).append( '=' )                     // 6.0.2.5 (2014/10/31) char を append する。
    405                                                .append( StringUtil.urlEncode( vv ) );
    406                                }
    407
    408                                // 3.8.0.8 (2005/10/03) BACK_GAMENID があれば BACK_ROW を追加する。
    409                                if( "BACK_GAMENID".equalsIgnoreCase( kk ) && rowNo >= 0 ) {
    410                                        strURL.append( "&BACK_ROW=" ).append( rowNo );
    411                                }
    412
    413                                // 6.2.4.0 (2015/05/15) 個別に GAMENID があれば、submitTag で指定されたはず。
    414                                if( "GAMENID".equalsIgnoreCase( kk ) ) {
    415                                        isGamenIdFlag = true;
    416                                }
    417                        }
    418                        // 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
    419                        // 取りあえず対象のリクエストは、Mapにセットしておきます。
    420                        if( isRedirectHidden && key != null && !key.startsWith( "h_" ) && !key.startsWith( "hX_" ) ) {
    421                                hiddenMap.put( key,getRequestValue( key,false ) );
    422                        }
    423                }
    424
    425                // 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
    426                // 個別に GAMENID がある場合(isGamenIdFlag == true)のみ、このフラグを有効にします。
    427                if( isRedirectHidden && isGamenIdFlag ) {
    428                        for( final Map.Entry<String,String> entry : hiddenMap.entrySet() ) {
    429                                final String key = '&' + entry.getKey() + '=';          // strURL は、必ず & で始まっている。
    430                                if( strURL.indexOf( key ) < 0 ) {                               // すでに、個別定義済みのパラメータは転送しない。
    431                                        strURL.append( key ).append( StringUtil.urlEncode( entry.getValue() ) );
    432                                }
    433                        }
    434                }
    435
    436                return XHTMLTag.addUrlEncode( forwardPage,strURL.toString() );
    437        }
    438
    439        /**
    440         * ページを リダイレクトかフォワードか選択します。
    441         * 判定条件は、拡張子や、選択件数などを加味して以下の判定を順次テストします。
    442         *
    443         *   FORWARD :
    444         *       アドレスが、 null か、.jsp を含み、"/" が入っていない場合
    445         *   REDIRECT:
    446         *       アドレスが、.jsp を含まないか、それ以外(.jsp を含み、"/" も含む)で、選択数が１件のみの場合
    447         *   COUNT_0 :
    448         *       それ以外で、選択数が０件の場合
    449         *   COUNT_N :
    450         *       それ以外で、選択数が１件以上の場合、または、その他。
    451         *
    452         * @og.rev 3.5.5.2 (2004/04/02) 新規作成：isJspPrefix( String page ) の代用です。
    453         * @og.rev 4.0.0.0 (2007/05/23) useTableData の書き換えを中止します。
    454         * @og.rev 4.0.0.0 (2007/11/12) 非選択状態でもリダイレクト可能なフラグ(isRedirectCheck)を追加
    455         *
    456         * @param   page        判定する転送先アドレス
    457         *
    458         * @return  FORWARD,REDIRECT,COUNT_0,COUNT_N のうち、どれか
    459         */
    460        private int selectResponseMethod( final String page ) {
    461                if( page == null ) { return FORWARD; }
    462
    463                final int adrs = page.indexOf( ".jsp" );
    464
    465                if( adrs >= 0 && page.lastIndexOf( '/',adrs ) < 0 ) {
    466                        return FORWARD;
    467                }
    468                else if( adrs < 0 || !isRedirectCheck ) { // 4.0.0.0 (2007/11/12) 非選択リダイレクト許可フラグ追加
    469                        return REDIRECT;
    470                }
    471                else if( rowCount == 1 ) {
    472                        return REDIRECT;
    473                }
    474                else if( rowCount == 0 ) {
    475                        return COUNT_0;
    476                }
    477                else {
    478                        return COUNT_N;
    479                }
    480        }
    481
    482        /**
    483         * 【TAG】(通常は使いません)sessionから所得する DBTableModelオブジェクトの ID
    484         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
    485         *
    486         * @og.tag
    487         * 表示処理後に，(内部ポインタを書き換えた)DBTableModelオブジェクトを
    488         * 同じキーで、sessionに登録します。
    489         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
    490         *
    491         * @og.rev 3.5.5.2 (2004/04/02) 新規追加
    492         * @og.rev 3.5.5.8 (2004/05/20) 内部仮想リクエスト Map を参照できるようにする。
    493         *
    494         * @param       id テーブルID (sessionから所得する時のID)
    495         */
    496        public void setTableId( final String id ) {
    497                // 注意:引数のリクエスト変数が無ければ、自分自身({@XXX}形式)を再セットする。
    498                tableIdTemp   = nval( getRequestParameter( id ),id );
    499        }
    500
    501        /**
    502         * 指定のスコープの内部キャッシュ情報に、DBTableModel の選択された値を登録します。
    503         *
    504         * 複数選択行が存在する場合は、先頭行を処理します。ただし、action="APPEND"の
    505         * 場合は、separator属性で指定された文字を使用して、連結します。
    506         *
    507         * @og.rev 3.5.5.2 (2004/04/02) 新規作成
    508         * @og.rev 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
    509         * @og.rev 3.8.0.4 (2005/08/08) dbkeys が null の場合に全件取得していた処理を中止します。
    510         * @og.rev 3.8.0.8 (2005/10/03) dbkeys が null の場合に全件取得していた処理を復活します。
    511         * @og.rev 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
    512         * @og.rev 3.8.5.1 (2006/04/28) dbkeys が null の場合に全件取得するかどうかを COMMON_FORWARD_DBKEYS_NULL_ALL で指定します。
    513         * @og.rev 4.0.0.0 (2007/05/23) tableId をこのメソッド内で求めます。
    514         *
    515         * @return      DBTableModelの選択された値の文字列
    516         * @og.rtnNotNull
    517         */
    518        private String getTableUrlData() {
    519                final String tableId = nval( getSubmitRequestParameter( tableIdTemp ),HybsSystem.TBL_MDL_KEY );
    520
    521                final DBTableModel table = (DBTableModel)getSessionAttribute( tableId );
    522
    523                String dbkeysUrl  = "";
    524                String tblkeysUrl = "";
    525
    526                if( table != null ) {
    527                        String[] keys = null;
    528                        String[] vals = null;
    529                        if( dbkeys != null ) {
    530                                keys = StringUtil.csv2Array( dbkeys );
    531                                vals = new String[keys.length];
    532                                for( int i=0; i<keys.length; i++ ) {
    533                                        final int clmNo = table.getColumnNo( keys[i] );
    534                                        vals[i] = table.getValue(rowNo,clmNo);
    535                                }
    536                        }
    537
    538                        dbkeysUrl = XHTMLTag.urlEncode( keys, vals );
    539
    540                        // 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
    541                        final int size = submitTableMap.size();
    542                        if( size > 0 ) {
    543                                @SuppressWarnings("rawtypes")
    544                                final Map.Entry[] entry = submitTableMap.entrySet().toArray( new Map.Entry[size] );
    545
    546                                String[] tblkeys = new String[size];
    547                                String[] tblvals = new String[size];
    548
    549                                for( int i=0; i<size; i++ ) {
    550                                        tblkeys[i]  = (String)entry[i].getKey();
    551                                        final String temp = (String)entry[i].getValue();
    552                                        final int clmNo = table.getColumnNo( temp );
    553                                        tblvals[i] = table.getValue( rowNo,clmNo );
    554                                }
    555                                tblkeysUrl = XHTMLTag.urlEncode( tblkeys, tblvals );
    556                        }
    557                }
    558
    559                String rtn = dbkeysUrl;
    560
    561                if( tblkeysUrl.length() > 0 ) {
    562                        if( rtn.length() > 0 ) {
    563                                rtn += "&" + tblkeysUrl;
    564                        }
    565                        else {
    566                                rtn = tblkeysUrl ;
    567                        }
    568                }
    569                return rtn ;
    570        }
    571
    572        /**
    573         * 【TAG】DBキーをCSV形式でセットします。
    574         *
    575         * @og.tag
    576         * URI の引数にセットするキーを CSV形式でセットします。
    577         * ここの指定は,DBTableModel 上のデータを取り込みます。
    578         *
    579         * @og.rev 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
    580         * @og.rev 3.5.5.8 (2004/05/20) 内部仮想リクエスト Map を参照できるようにする。
    581         *
    582         * @param       key DBキー(CSV形式)
    583         */
    584        public void setDbkeys( final String key ) {
    585                // 注意:引数のリクエスト変数が無ければ、自分自身({@XXX}形式)を再セットする。
    586                dbkeysTemp = nval( getRequestParameter( key ),key ) ;
    587        }
    588
    589        /**
    590         * 内部の仮想リクエスト Map より、リクエストパラメータより値を設定します。
    591         *
    592         * submitタグの keys,vals より送信されたリクエスト値は、このクラスで
    593         * 処理され、内部の仮想リクエスト Map に保存されます。
    594         * 通常のリクエスト設定時点では、この値は取り出すことが出来ない為、
    595         * Map に保存(getForwardURI 処理で設定)された後に、引き出します。
    596         *
    597         * @og.rev 3.5.5.8 (2004/05/20) 新規作成
    598         *
    599         * @param       key     DBキー(CSV形式)
    600         *
    601         * @return      仮想リクエスト Map を反映させた、リクエスト値
    602         */
    603        private String getSubmitRequestParameter( final String key ) {
    604                String rtn = key;
    605
    606                // 変数が "{@XXXX}" の場合のみ対応
    607                if( key != null && key.startsWith( "{@" ) && key.charAt(key.length()-1) == '}' ) {
    608                        rtn = submitRequestMap.get( key.substring( 2,key.length()-1 ) );
    609                }
    610
    611                return rtn;
    612        }
    613
    614        /**
    615         * 【TAG】非選択状態の場合にforwardを許可するかどうか[true/false]を指定します(初期値:true)。
    616         *
    617         * @og.tag
    618         * 初期値はtrueが設定されています
    619         * falseにすると許可されます
    620         *
    621         * @og.rev 4.0.0.0 (2007/11/09) 新規作成
    622         *
    623         * @param       flag    非選択状態のforward許可 [true:不許可/false:許可]
    624         */
    625        public void setUseRedirectCheck(final String flag) {
    626                isRedirectCheck = nval( getRequestParameter( flag ),isRedirectCheck );
    627        }
    628
    629        /**
    630         * 【TAG】redirectの処理時に、hiddenパラメータを転送するかどうか[true:する/false:しない]を指定します(初期値:false)。
    631         *
    632         * @og.tag
    633         * submitTag で、gamenId を指定すると、redirect されます。
    634         * その場合、従来の画面では、hidden 属性が転送されていなかったため、
    635         * 個別に keys,vals で指定する必要がありました。
    636         * hidden で記述した値は、redirect でも転送できるようにします。
    637         * なお、個別定義済みのパラメータがあり、hiddenパラメータを同じキーの場合は、
    638         * 個別パラメータを優先し、hiddenパラメータは転送しません。
    639         *
    640         * 本来は、gamenId指定の submit の場合でも、必要最小限の値のみ転送すべきで、
    641         * この値を true にするのは、過去のJSPソースを、修正せずに使いたいケースに限定してください。
    642         * 互換性の関係で、初期値は、false にします。
    643         *
    644         * @og.rev 6.2.4.0 (2015/05/15) 新規作成
    645         *
    646         * @param       flag    非選択状態のforward許可 [true:不許可/false:許可]
    647         */
    648        public void setUseRedirectHidden(final String flag) {
    649                isRedirectHidden = nval( getRequestParameter( flag ),isRedirectHidden );
    650        }
    651
    652        /**
    653         * 【TAG】ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
    654         *
    655         * @og.tag
    656         * 通常のｴﾗｰﾒｯｾｰｼﾞは、ﾗﾍﾞﾙ（長）が使われますが、これをﾗﾍﾞﾙ（短）を使いたい場合に、true にセットします。
    657         * ここでのラベル（短）は、ﾀｸﾞ修飾なしの、ﾗﾍﾞﾙ（短）です。
    658         * 標準はfalse:利用しない=ﾗﾍﾞﾙ（長）です。
    659         * true/false以外を指定した場合はfalse扱いとします。
    660         *
    661         * ﾗﾍﾞﾙﾘｿｰｽの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
    662         *
    663         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
    664         *
    665         * @param prm SLABEL利用 [true:利用する/false:利用しない]
    666         */
    667        public void setUseSLabel( final String prm ) {
    668                useSLabel = nval( getRequestParameter( prm ),useSLabel );
    669        }
    670
    671        /**
    672         * このオブジェクトの文字列表現を返します。
    673         * 基本的にデバッグ目的に使用します。
    674         *
    675         * @return このクラスの文字列表現
    676         * @og.rtnNotNull
    677         */
    678        @Override
    679        public String toString() {
    680                return ToString.title( this.getClass().getName() )
    681                                .println( "VERSION"                             ,VERSION                        )
    682                                .println( "rowCount"                    ,rowCount                       )
    683                                .println( "rowNo"                               ,rowNo                          )
    684                                .println( "dbkeys"                              ,dbkeys                         )
    685                                .println( "tableIdTemp"                 ,tableIdTemp            )
    686                                .println( "dbkeysTemp"                  ,dbkeysTemp                     )
    687                                .println( "useRedirectCheck"    ,isRedirectCheck        )       // 6.2.4.0 (2015/05/15)
    688                                .println( "useRedirectHidden"   ,isRedirectHidden       )       // 6.2.4.0 (2015/05/15)
    689                                .println( "Other..."                    ,getAttributes().getAttribute() )
    690                                .fixForm().toString() ;
    691        }
    692}