    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.fukurou.system.OgBuilder ;                          // 6.4.4.1 (2016/03/18)
    019
    020import org.opengion.fukurou.util.TagBuffer;
    021import org.opengion.fukurou.util.XHTMLTag;
    022import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
    023import org.opengion.hayabusa.common.HybsSystemException;
    024import org.opengion.hayabusa.db.DBTableModel;
    025import org.opengion.hayabusa.db.DBTableModelUtil;
    026
    027import static org.opengion.fukurou.util.StringUtil.nval;
    028
    029import java.io.IOException;
    030import java.io.ObjectInputStream;
    031import java.util.ArrayList;
    032import java.util.List;
    033import java.util.Locale;
    034import java.util.Arrays;                                                                                // 6.4.3.4 (2016/03/11)
    035import java.util.stream.Collectors;                                                             // 6.4.3.4 (2016/03/11)
    036
    037/** タブ形式のリンクを表示するタグです。
    038 *
    039 * このタグ形式の実態は、リンクのリストであるため、実の画面の表示はターゲットを指定して
    040 * 別フレームで行う必要があります。
    041 *
    042 * タブの指定方法については、listType属性の指定により、クエリ発行(DB)により動的に生成する
    043 * パターンと、タグ指定(TAG)により、静的に生成するパターンがあります。
    044 * listType属性に何も指定されていない場合は、Body部分に記述された内容により、自動判定されます。
    045 * ("SELECT"で始まっている場合はDB、それ以外はTAGとして処理)
    046 *
    047 * ①listType属性が"DB"の場合
    048 *  検索された各カラムは、その順番により次の意味を持ちます。
    049 *  [第1カラム] タブの名前        : リンク時のキー情報、後述のopenTabName属性のキーとしても使用 ※必須
    050 *  [第2カラム] タブの表示名称    : タブの表示名称 指定がない場合は、第1カラムが表示名称となります。
    051 *  [第3カラム] タブのリンク先URL : タブのリンク先URL 指定がない場合は、href属性の値が適用されます。
    052 *  [第4カラム] タブのクラス属性  : 個別のタブに付加されるクラス属性 指定がない場合は、unselClass属性の値が適用されます。
    053 *  [第5カラム] タブのロールズ    : タブのロールズを指定します。ユーザーロールズと一致しない指定した場合は、タブが表示されなくなります。
    054 *  [第6カラム] タブの選択可否    : タブの選択可否を'true'or'false'で指定します。falseを指定した場合は、タブが表示されなくなります。
    055 *                                  (ロールズで選択不可になっている場合は、この値は無視されます)
    056 *  各カラムの値は[カラム名]=[値]の形で、リンク先のJSPに引数として渡されます。
    057 *  また、リンク先のJSPについては、href属性で指定します。
    058 *
    059 * ②listType属性が"TAG"の場合
    060 *  tabListタグを記述し、個別にタブを定義します。
    061 *  制御可能な項目は、①DBの場合と同等です。
    062 *  タブの名前を定義するname属性は、tabListタグで必ず定義する必要があります。
    063 *  lbl属性が指定されていない場合は、name属性のラベル名称を取得します。
    064 *  タブのリンク先JSP及び、クラス属性については、tabListタグで指定がない場合、tabListタグの値が適用されます。
    065 *
    066 * [共通設定]
    067 * 初期設定では、第1番目の"有効な"タブが自動的に開かれます。(="true")
    068 * 各タブの表示方法で、選択不可能なタブが存在している場合は、それらを読み飛ばした上で、"有効な"タブを
    069 * 検索します。
    070 * また、自動で開くタブは、openTabName属性で指定可能であり、これに変数を定義することで、
    071 * 画面リロード時も、開いていたタブを再度選択された状態で表示することが可能です。
    072 *
    073 * 7.4.1.0 (2021/04/23)
    074 *   openTab = false を指定すると、自動でオープンしません。
    075 *
    076 * 選択したタブ及び非選択のタブの枠線や、背景色等を変更する場合は、custom.cssでクラスを定義し、
    077 * 変更して下さい。
    078 *
    079 * タブの表示方向(水平方向 or 垂直方向)については、orientation属性で変更することが可能です。
    080 * (初期値は、水平方向)
    081 * 水平方向にした場合は、listCount属性により強制的に一定数のタブを表示する毎に、改行を挿入することができます。
    082 *
    083 * このタグを使用する場合は、headタグで必ずuseTabLink="true"を指定してJavaScriptをロードして下さい。
    084 *
    085 * 各属性は、{&#064;XXXX} 変数が使用できます。
    086 * これは、ServletRequest から、XXXX をキーに値を取り出し,この変数に割り当てます。
    087 * つまり、このXXXXをキーにリクエストすれば、この変数に値をセットすることができます。
    088 *
    089 * @og.formSample
    090 * ●形式：&lt;og:tabLink href="…" … /&gt;
    091 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    092 *
    093 * ●Tag定義：
    094 *   &lt;og:tabLink
    095 *       listType           【TAG】タブの一覧をどこから取得するかを指定します(初期値:AUTO)
    096 *       href               【TAG】リンク先のJSPを指定します(初期値:result.jsp)
    097 *       target             【TAG】リンクのターゲットを指定します(初期値:RESULT)
    098 *       openTab            【TAG】リンク表示にタブリンクを自動で開くかどうか[true/false]を指定します(初期値:true[=開く])
    099 *       openTabName        【TAG】最初に開くタブリンクの名前を指定します
    100 *       constKeys          【TAG】次画面に渡す定数パラメーターのキーを指定します
    101 *       constVals          【TAG】次画面に渡す定数パラメーターの値を指定します
    102 *       listCount          【TAG】1行辺りに表示するタブの数を指定します(初期値:10)
    103 *       selClass           【TAG】選択タブのクラスを指定します(初期値:selTab)
    104 *       unselClass         【TAG】非選択タブのクラスを指定します(初期値:unselTab)
    105 *       orientation        【TAG】タブの方向、横型(Horizontal)か縦型(Vertical)を指定します(初期値:横型)
    106 *       width              【TAG】タブリンクの幅を % 、px 、または "auto" で指定します
    107 *       height             【TAG】タブの高さを、% 、px 、または "auto" で指定します
    108 *       onClick            【HTML】JavaScriptのイベント onClick を設定します(初期値：:onClick="changeTab( this, 'selTab' );") 7.4.1.0 (2021/04/23)
    109 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 7.4.1.0 (2021/04/23)
    110 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 7.4.1.0 (2021/04/23)
    111 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 7.4.1.0 (2021/04/23)
    112 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 7.4.1.0 (2021/04/23)
    113 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 7.4.1.0 (2021/04/23)
    114 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    115 *   &gt;   ... Body ...
    116 *   &lt;/og:tabLink&gt;
    117 *
    118 * ●使用例
    119 *   ①DBからタブリストを取得する場合
    120 *
    121 *    Body部分に記述されたクエリよりタブ一覧を生成します。
    122 *
    123 *      &lt;og:tabLink
    124 *          listType        = "DB"                      タブの一覧をどこから取得するか
    125 *          href            = "result.jsp"              リンク先のJSP
    126 *          target          = "RESULT"                  リンクターゲット
    127 *          openTab         = "[true/false]"            タブ表示後にタブを自動で開く
    128 *          openTabName     = "{&#064;PN}               自動で開くタブの名前
    129 *          constKeys       = "KEY1"                    次画面に固定で渡すキー一覧
    130 *          constVals       = "{&#064;VAL1}"            次画面に固定で渡す値一覧
    131 *          listCount       = "10"                      1行辺りに表示するタブの数
    132 *          selClass        = "selTab"                  選択タブのクラス
    133 *          unselClass      = "unselTab"                非選択タブのクラス
    134 *          width           = "100px"                   タブリンクの幅
    135 *          height          = "50px"                    タブリンクの高さ
    136 *       &gt;
    137 *               select PN,HINM,'tabClass','query.jsp','ADMIN','false' from XX01 where PN = '{&#064;PN}' order by PN
    138 *      &lt;/og:tabLink&gt;
    139 *
    140 *   ②tabListタグからタブリストを生成する場合
    141 *
    142 *    tabListタグよりタブ一覧を生成します。
    143 *
    144 *      &lt;og:tabLink
    145 *          listType        = "DB"                      タブの一覧をどこから取得するか
    146 *          href            = "result.jsp"              リンク先のJSP
    147 *          target          = "RESULT"                  リンクターゲット
    148 *          openTab         = "[true/false]"            タブ表示後にタブを自動で開く
    149 *          openTabName     = "{&#064;PN}               自動で開くタブの名前
    150 *          constKeys       = "KEY1"                    次画面に固定で渡すキー一覧
    151 *          constVals       = "{&#064;VAL1}"            次画面に固定で渡す値一覧
    152 *          listCount       = "10"                      1行辺りに表示するタブの数
    153 *          selClass        = "selTab"                  選択タブのクラス
    154 *          unselClass      = "unselTab"                非選択タブのクラス
    155 *          width           = "100px"                   タブリンクの幅
    156 *          height          = "50px"                    タブリンクの高さ
    157 *       &gt;
    158 *          &lt;og:tabList name="TAB1" href="result1.jsp" keys="PN,CDK" vals="ABC,V" /&gt;
    159 *          &lt;og:tabList name="TAB2" href="result2.jsp" keys="PN,CDK" vals="BCD,W" /&gt;
    160 *          &lt;og:tabList name="TAB3" href="result3.jsp" keys="PN,CDK" vals="CDE,X" /&gt;
    161 *      &lt;/og:tabLink&gt;
    162 *
    163 * @og.group 画面表示
    164 *
    165 * @version  0.9.0      2008/09/26
    166 * @author       Nakamura
    167 * @since        JDK1.4,
    168 */
    169public class TabLinkTag extends CommonTagSupport {
    170        private static final String VERSION = "7.4.1.0 (2021/04/23)" ;
    171        private static final long serialVersionUID = 741020210423L ;
    172
    173        /** リストのulタグのclass属性 */
    174        private static final String             UL_TAG_START            = "<ul class=\"tabList\">";
    175        private static final String             UL_TAG_END                      = "</ul>";
    176
    177        /** タブ表示を入れ替えるためのJavaScript関数 */
    178        private static final String             CHANGE_TAB_SCRIPT       = "changeTab";
    179        private static final String             INITIAL_TAB_SCRIPT      = "initialTabSelect";
    180
    181        /** 自動で開くタブに付加されるID */
    182        private static final String             FIRST_TAB_ID            = "firstTab";
    183
    184        /** リスト取得タイプのEnum */
    185        private enum LIST_TYPE { AUTO, DB, TAG };
    186//      private static enum LIST_TYPE { AUTO, DB, TAG };
    187
    188        /** 内部変数 */
    189        private String          query                   ;
    190        private transient List<TabData>         tabData         = new ArrayList<>();
    191
    192        /** タグで設定する属性 */
    193        private LIST_TYPE       type                    = LIST_TYPE.AUTO;
    194        private String          href                    = "result.jsp";
    195        private String          target                  = "RESULT";
    196        private boolean         openTab                 = true;
    197        private String          openTabName             ;
    198        private String[]        constKeys               ;
    199        private String[]        constVals               ;
    200        private int                     listCount               = 10;
    201        private String          selClass                = "selTab";
    202        private String          unselClass              = "unselTab";
    203        private boolean         isHorizontal    = true;
    204        private String          width                   = "auto";
    205        private String          height                  = "auto";
    206        private String          onClick                 = CHANGE_TAB_SCRIPT;            // 7.4.1.0 (2021/04/23)
    207
    208        /**
    209         * デフォルトコンストラクター
    210         *
    211         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    212         */
    213        public TabLinkTag() { super(); }                // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    214
    215        /**
    216         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
    217         *
    218         * @og.rev 7.4.1.0 (2021/04/23) caseKey,caseVal,caseNN,caseNull,caseIf 属性を追加
    219         *
    220         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
    221         */
    222        @Override
    223        public int doStartTag() {
    224                // 7.4.1.0 (2021/04/23) caseKey,caseVal,caseNN,caseNull,caseIf 属性を追加
    225                if( !useTag() ) { return SKIP_BODY ; }
    226
    227                return EVAL_BODY_BUFFERED ;     // Body を評価する
    228        }
    229
    230        /**
    231         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
    232         *
    233         * @og.rev 6.3.1.1 (2015/07/10) BodyString,BodyRawStringは、CommonTagSupport で、trim() します。
    234         *
    235         * @return      後続処理の指示(SKIP_BODY)
    236         */
    237        @Override
    238        public int doAfterBody() {
    239                query = getBodyString();
    240                return SKIP_BODY ;
    241        }
    242
    243        /**
    244         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    245         *
    246         * @og.rev 4.3.5.0 (2008/02/01) 処理及び内部構造を大幅に見直し
    247         * @og.rev 7.4.1.0 (2021/04/23) caseKey,caseVal,caseNN,caseNull,caseIf 属性を追加
    248         *
    249         * @return      後続処理の指示
    250         */
    251        @Override
    252        public int doEndTag() {
    253                debugPrint();
    254
    255                // 7.4.1.0 (2021/04/23) caseKey,caseVal,caseNN,caseNull,caseIf 属性を追加
    256                if( !useTag() ) { return EVAL_PAGE ; }
    257
    258//              final int rtnCode = EVAL_PAGE;          // 7.4.1.0 (2021/04/23) 書き換えてないので、直接指定します。
    259
    260                // 種別の自動判定処理
    261                if( type == LIST_TYPE.AUTO ) {
    262                        if( query == null || query.isEmpty() ) {
    263                                type = LIST_TYPE.TAG;
    264                        }
    265                        else {
    266                                if( query.toUpperCase( Locale.JAPAN ).indexOf( "SELECT" ) >= 0 ) {
    267                                        type = LIST_TYPE.DB;
    268                                }
    269                                else {
    270                                        type = LIST_TYPE.TAG;
    271                                }
    272                        }
    273                }
    274
    275                if( type == LIST_TYPE.DB ) {
    276                        makeTabsFromQuery();
    277                }
    278                else if( type == LIST_TYPE.TAG ) {
    279                        makeTabsFromTag();
    280                }
    281
    282                // リンク一覧が何も設定されていない場合は、処理しない
    283                if( ! tabData.isEmpty() ) {
    284                        makeTag();
    285                }
    286
    287//              return rtnCode ;
    288                return EVAL_PAGE ;              // 7.4.1.0 (2021/04/23)
    289        }
    290
    291        /**
    292         * タグリブオブジェクトをリリースします。
    293         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    294         *
    295         * @og.rev 7.4.1.0 (2021/04/23) タブをクリックした際のJavaScriptを外部から設定できるようにします。
    296         */
    297        @Override
    298        protected void release2() {
    299                super.release2();
    300                query                   = null;
    301                tabData                 = new ArrayList<>();
    302                type                    = LIST_TYPE.AUTO;
    303                href                    = "result.jsp";
    304                target                  = "RESULT";
    305                openTab                 = true;
    306                openTabName             = null;
    307                constKeys               = null;
    308                constVals               = null;
    309                listCount               = 10;
    310                selClass                = "selTab";
    311                unselClass              = "unselTab";
    312                isHorizontal    = true;
    313                width                   = "auto";
    314                height                  = "auto";
    315                onClick                 = CHANGE_TAB_SCRIPT;            // 7.4.1.0 (2021/04/23)
    316        }
    317
    318        /**
    319         * DBからタブリンクの一覧を作成します。
    320         * DBTableModelが作成されない(行数が0の場合)は、リンク一覧は生成されません。
    321         *
    322         * @og.rev 6.3.9.1 (2015/11/27) ３項演算子を || or &amp;&amp; で簡素化できる(PMD)。
    323         */
    324        private void makeTabsFromQuery() {
    325                final DBTableModel table = DBTableModelUtil.makeDBTable( query, new String[0], getResource(), getApplicationInfo() );
    326                if( table == null || table.getRowCount() == 0 ) {
    327                        return;
    328                }
    329
    330                // 6.3.9.1 (2015/11/27) 初期値設定の簡素化
    331                final boolean isSetLabel        = table.getColumnCount() > 1;
    332                final boolean isSetHref         = table.getColumnCount() > 2;
    333                final boolean isSetClazz        = table.getColumnCount() > 3;
    334                final boolean isSetRoles        = table.getColumnCount() > 4;
    335                final boolean isSetVisible      = table.getColumnCount() > 5;
    336
    337                final int rowCount = table.getRowCount();
    338                final String key   = table.getColumnName( 0 );
    339                // 6.3.9.1 (2015/11/27) visible=true以外は処理しないので、先に求める。
    340                for( int row=0; row<rowCount; row++ ) {
    341                        final boolean visible = ( !isSetRoles   || getUser().isAccess( table.getValue( row, 4 ) ) ) &&
    342                                                                        ( !isSetVisible || Boolean.valueOf(    table.getValue( row, 5 ) ) );
    343
    344                        if( visible ) {
    345                                final String value      = table.getValue( row, 0 );
    346                                final String label      = ( isSetLabel ? nval( table.getValue( row, 1 ), value ) : value );
    347                                String newHref  = ( isSetHref  ? nval( table.getValue( row, 2 ), href ) : href );
    348                                // 第1カラムのカラム名とその値はリンクの引数に含める
    349                                newHref = XHTMLTag.addUrlEncode( newHref, XHTMLTag.urlEncode( key, value ) );
    350
    351                                final String clazz      = ( isSetClazz ? nval( table.getValue( row, 3 ), unselClass ) : unselClass );
    352
    353                                tabData.add( new TabData( newHref, value, label, clazz, visible ) );
    354                        }
    355                }
    356        }
    357
    358        /**
    359         * タブリストからタブリンクの一覧を作成します。
    360         * (予めaddTagメソッドにより、リンク一覧が登録されているため、ここでは何も処理しません)
    361         *
    362         * @see #addTag( String, String, String, String, boolean, String[], String[] )
    363         */
    364        private void makeTabsFromTag() {
    365                // 何もありません。(PMD エラー回避)
    366        }
    367
    368        /**
    369         * 子タグであるタブリストタグからタブ情報をセットします。
    370         *
    371         * @param hr 画面URL
    372         * @param name タブの名前
    373         * @param label タブの表示名称
    374         * @param clz 非選択状態のタブに付加するclass名
    375         * @param visible タブが選択可能 [true:可能/false:不可能]
    376         * @param keys リンク先のJSPに渡すキー一覧
    377         * @param vals リンク先のJSPに渡す値一覧
    378         */
    379        protected void addTag( final String hr, final String name, final String label, final String clz
    380                                                        ,final boolean visible, final String[] keys, final String[] vals ) {
    381                String newHref = nval( hr, href );
    382                if( keys != null && keys.length > 0 ) {
    383                        newHref = XHTMLTag.addUrlEncode( newHref, XHTMLTag.urlEncode( keys, vals ) );
    384                }
    385
    386                if( visible ) { // visible=falseの場合は表示しない
    387                        tabData.add( new TabData( newHref, name, nval( label, getLabel( name ) ), nval( clz, unselClass ), visible ) );
    388                }
    389        }
    390
    391        /**
    392         * リンク一覧からHTMLタグを作成します。
    393         *
    394         * @og.rev 5.0.2.0 (2009/11/01) openTab属性がfalseの場合でも、openTabNameに指定されたタブに色付けする。
    395         * @og.rev 5.1.4.0 (2010/03/01) FF対応&FirstTabのID付加に関するバグを修正
    396         * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。
    397         * @og.rev 7.4.1.0 (2021/04/23) openTab=false の場合は、自動でオープンしません。
    398         */
    399        private void makeTag() {
    400                final OgBuilder buf = new OgBuilder().appendCR();
    401
    402                boolean isExistFirst = false;
    403                for( int idx=0; idx<tabData.size(); idx++ ) {
    404                        final TabData tab = tabData.get( idx );
    405
    406                        if( idx % listCount == 0 ) {
    407                                buf.appendIfCR( idx > 0 , UL_TAG_END )                          // if
    408                                        .appendCR( UL_TAG_START );
    409                        }
    410
    411                        // openTabNameが定義されていない場合は、1番目の有効なタブを開く
    412                        // 5.1.4.0 (2010/03/01) バグ修正
    413                        if( !isExistFirst && tab.visible
    414                                && ( openTabName == null || openTabName.isEmpty() || openTabName.equals( tab.name ) ) ) {
    415                                // 7.4.1.0 (2021/04/23) 選択タブのクラスをセットします。
    416                                final TabData tab2 = tab.copyTab( selClass );
    417
    418//                              buf.append( tab.makeLiTag( idx, true ) );
    419                                buf.append( tab2.makeLiTag( idx, true ) );
    420                                isExistFirst = true;
    421                        }
    422                        else {
    423                                buf.append( tab.makeLiTag( idx, false ) );
    424                        }
    425                }
    426                // 7.4.1.0 (2021/04/23) openTab=false の場合は、自動でオープンしません。
    427                buf.appendCR( UL_TAG_END )
    428//                      .appendIf( openTab || openTabName != null && openTabName.length() > 0   // 6.9.7.0 (2018/05/14) PMD Useless parentheses.
    429                        .appendIf( openTab && openTabName != null && openTabName.length() > 0   // 6.9.7.0 (2018/05/14) PMD Useless parentheses.
    430                                        // 8.1.0.0 (2021/12/28) HTML5 準拠に見直し(<script> type属性削除)
    431//                                      , "<script type=\"text/javascript\">addEvent(window,\"load\", function() { "
    432                                        , "<script>addEvent(window,\"load\", function() { "
    433                                        , INITIAL_TAB_SCRIPT
    434                                        , "(" , String.valueOf( openTab ) , "); } );</script>" );
    435
    436                jspPrint( buf.toString() );
    437        }
    438
    439        /**
    440         * Tabデータ を管理している 内部クラス
    441         *
    442         * タブの情報を管理するための簡易的な、内部クラスです。
    443         */
    444        private final class TabData {
    445                // 引数として初期設定される変数
    446                private final String href;
    447                private final String name;
    448                private final String label;
    449                private final String clazz;
    450                // 現状の実装では、visible=falseは渡ってきませんが、将来的にdisableの状態で
    451                // 表示したい場合等に対応するため残しておきます。
    452                private final boolean visible;
    453
    454                /**
    455                 * コンストラクタ
    456                 *
    457                 * @param hr 画面URL
    458                 * @param nm タブの名前
    459                 * @param lbl タブの表示名称
    460                 * @param clz 非選択状態のタブに付加するclass名
    461                 * @param vsb タブが選択可能 [true:可能/false:不可能]
    462                 */
    463                public TabData( final String hr, final String nm, final String lbl, final String clz, final boolean vsb ) {
    464                        href    = hr;
    465                        name    = nm;
    466                        label   = lbl;
    467                        clazz   = clz;
    468                        visible = vsb;
    469                }
    470
    471                /**
    472                 * class名違いの新しいTabDataを作成します。
    473                 *
    474                 * 通常、選択状態のｸﾗｽ名を付ける場合に、使用します。
    475                 *
    476                 * @param clz タブに付加するclass名
    477                 * @return class名違いの新しいTabData
    478                 */
    479                public TabData copyTab( final String clz ) {
    480                        return new TabData( href,name,label,clz,visible );
    481                }
    482
    483                /**
    484                 * liタグの部分の文字列を生成します。
    485                 *
    486                 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。
    487                 *
    488                 * @param idx           生成したタブのインデックス番号
    489                 * @param isFirst       始めの有効なタブかどうか[true:始め/false:それ以外]
    490                 * @return liタグ文字列
    491                 * @og.rtnNotNull
    492                 */
    493                private String makeLiTag( final int idx, final boolean isFirst ) {
    494                        return new OgBuilder()
    495                                .append( "<li class=\"" , clazz , "\""
    496                                                , " style=\""
    497                                                , " width: "    , width  , ";"
    498                                                , " height: "   , height , ";" )
    499                                .appendIf( isHorizontal , " float: left;" )
    500                                .appendIf( isHorizontal && ( idx % listCount == 0 )     // if-if なので、少し無駄かも
    501                                                , " clear: left; margin-left: "
    502                                                , String.valueOf( (idx/listCount) * 10 )
    503                                                , "px;" )
    504                                .appendCR( " \">" , makeLinkTag( isFirst ) , "</li>" )
    505                                .toString();
    506                }
    507
    508                /**
    509                 * aタグの部分の文字列を生成します。
    510                 * タブが選択不可能な状態の場合は、タブの表示文字列をそのまま返します。
    511                 *
    512                 * @og.rev 4.3.6.4 戻るボタンがでない問題への対応
    513                 * @og.rev 7.4.1.0 (2021/04/23) タブをクリックした際のJavaScriptを外部から設定できるようにします。
    514                 *
    515                 * @param       isFirst 始めの有効なタブかどうか[true:始め/false:それ以外]
    516                 * @return      liタグ文字列
    517                 * @og.rtnNotNull
    518                 */
    519                private String makeLinkTag( final boolean isFirst ) {
    520                        String newHref = XHTMLTag.addUrlEncode( href, XHTMLTag.urlEncode( constKeys, constVals ) );
    521                        // 4.3.6.4 (2009/05/01)
    522                        // タブ画面から遷移した時に、タブの読込により、画面IDが消えてしまい
    523                        // 戻るボタンがでない不具合への対応
    524                        newHref = XHTMLTag.addUrlEncode( newHref, "GAMENID=" + getGUIInfoAttri( "KEY" ) );
    525
    526                        // 7.4.1.0 (2021/04/23) タブをクリックした際のJavaScriptを外部から設定できるようにします。
    527                        if( CHANGE_TAB_SCRIPT.equals( onClick ) ) {                     // つまり、デフォルトのまま
    528                                onClick = CHANGE_TAB_SCRIPT + "( this, \"" + selClass + "\" );" ;
    529                        }
    530
    531                        // 6.1.1.0 (2015/01/17) TagBufferの連結記述
    532                        return new TagBuffer( "a" )
    533                                        .add( "href"    , newHref )
    534                                        .add( "name"    , name )
    535                                        .add( "target"  , target )
    536//                                      .add( "onClick" , CHANGE_TAB_SCRIPT + "( this, \"" + selClass + "\" );" )
    537                                        .add( "onClick" , onClick               , onClick != null )                     // 7.4.1.0 (2021/04/23) null 以外は追加する。
    538                                        .add( "id"              , FIRST_TAB_ID  , isFirst )
    539                                        .addBody( label )
    540                                        .makeTag();
    541                }
    542        }
    543
    544        /**
    545         * 【TAG】タブの一覧をどこから取得するか[AUTO/DB/TAG]を指定します(初期値:AUTO)。
    546         *
    547         * @og.tag
    548         * タブの一覧をどこから取得するかを指定します。
    549         * 現状の実装では、クエリを発行して一覧を生成する「DB」と、子タグである
    550         * tabListタグを列挙してタブを定義する「TAG」が実装されています。
    551         *
    552         * また、「AUTO」と指定した場合は、Body部分の内容に応じて自動的に判定されます。
    553         * 初期値は、｢AUTO」です。
    554         *
    555         * @og.rev 6.4.3.4 (2016/03/11) CSV形式の文字連結を、stream 経由で行います。
    556         *
    557         * @param       tp 取得方法 [AUTO/DB/TAG]
    558         */
    559        public void setListType( final String tp ) {
    560                final String typeStr = nval( getRequestParameter( tp ), null );
    561                try {
    562                        type = LIST_TYPE.valueOf( typeStr );
    563                }
    564                catch( final IllegalArgumentException ex ) {
    565                        final String errMsg = Arrays.stream( LIST_TYPE.values() )
    566                                                                        .map( obj -> obj.name() )
    567                                                                        .collect( Collectors.joining( "," , "listType は " , " から選んでください。" ) );  // 連結文字 , 最初 , 最後
    568                        throw new HybsSystemException( errMsg, ex );
    569                }
    570        }
    571
    572        /**
    573         * 【TAG】リンク先のJSPを指定します(初期値:result.jsp)。
    574         *
    575         * @og.tag
    576         * リンク先のJSPを指定します。
    577         * このタブリンクは、あくまで「タブの形をしたリンク」なので、
    578         * target属性と合わせてセットする必要があります。
    579         * 初期値は、｢result.jsp」です。
    580         *
    581         * @param       hr リンク先のJSP
    582         */
    583        public void setHref( final String hr ) {
    584                href = nval( getRequestParameter( hr ), href );
    585        }
    586
    587        /**
    588         * 【TAG】リンクのターゲットを指定します(初期値:RESULT)。
    589         *
    590         * @og.tag
    591         * リンクのターゲットを指定します。
    592         * このタブリンクは、あくまで「タブの形をしたリンク」なので、
    593         * target属性を設定し、別のフレームに実画面を表示するようにします。
    594         * 初期値は、｢RESULT」です。
    595         *
    596         * @param       tgt リンクターゲット
    597         */
    598        public void setTarget( final String tgt ) {
    599                target = nval( getRequestParameter( tgt ), target );
    600        }
    601
    602        /**
    603         * 【TAG】リンク表示にタブリンクを自動で開くかどうか[true/false]を指定します(初期値:true[=開く])。
    604         *
    605         * @og.tag
    606         * リンク表示にタブリンクを自動で開くかを指定します。
    607         * openTabName属性が指定されていない場合、自動で開くタブは
    608         * 「1番目に表示されたタブリンク」です。
    609         * 指定されている場合は、その名前を持つ「1番目」のタブが自動で開かれます。
    610         * タブが選択不可能な状態の場合は、「1番目」の条件から除外されます。
    611         * 初期値は、「true(開く)」です。
    612         *
    613         * 7.4.1.0 (2021/04/23)
    614         *   openTab = false を指定すると、自動でオープンしません。
    615         *
    616         * @param       flag 自動タブオープン [true:自動で開く/false:開かない]
    617         */
    618        public void setOpenTab( final String flag ) {
    619                openTab = nval( getRequestParameter( flag ), openTab );
    620        }
    621
    622        /**
    623         * 【TAG】最初に開くタブリンクの名前を指定します。
    624         *
    625         * @og.tag
    626         * 最初に開くタブリンクのキーを指定します。
    627         *
    628         * @param       name 初期表示タブ名前
    629         */
    630        public void setOpenTabName( final String name ) {
    631                openTabName = nval( getRequestParameter( name ), openTabName );
    632        }
    633
    634        /**
    635         * 【TAG】次画面に渡す定数パラメーターのキーを指定します。
    636         *
    637         * @og.tag
    638         * 次画面に渡す定数パラメーターのキーを指定します。
    639         * キーはCSV形式で複数指定が可能です。
    640         * パラメーターの値は、constVals属性の数と一致している必要があります。
    641         *
    642         * @param       keys 定数キー (CSV形式)
    643         * @see         #setConstVals( String )
    644         */
    645        public void setConstKeys( final String keys ) {
    646                constKeys = getCSVParameter( keys );
    647        }
    648
    649        /**
    650         * 【TAG】次画面に渡す定数パラメーターの値を指定します。
    651         *
    652         * @og.tag
    653         * 次画面に渡す定数パラメーターの値を指定します。
    654         * 値はCSV形式で複数指定が可能です。
    655         * パラメーターの値は、constKeys属性の数と一致している必要があります。
    656         *
    657         * @param       vals 定数値 (CSV形式)
    658         * @see         #setConstKeys( String )
    659         */
    660        public void setConstVals( final String vals ) {
    661                constVals = getCSVParameter( vals );
    662        }
    663
    664        /**
    665         * 【TAG】1行辺りに表示するタブの数を指定します(初期値:10)。
    666         *
    667         * @og.tag
    668         * 1行辺りに表示するタブの数を指定します。
    669         * 1行辺りのタブの数がこの設定を超えると、自動的に折り返します。
    670         * また、折り返し毎に、左に10pxのマージンを設けます。
    671         * 初期値は、10です。
    672         * この属性は、orientationがHorizontal(水平方向)の場合のみ有効です。
    673         *
    674         * @param       cnt 1行タブ数
    675         */
    676        public void setListCount( final String cnt ) {
    677                listCount = nval( getRequestParameter( cnt ), listCount );
    678        }
    679
    680        /**
    681         * 【TAG】選択タブのクラスを指定します(初期値:selTab)。
    682         *
    683         * @og.tag
    684         * タブが選択されている状態にある場合の、タブ部分のクラス名を指定します。
    685         * このクラス名を変更する場合は、そのクラスをcustom/custom.css等で再定義して下さい。
    686         * 初期値は、selTabです。
    687         *
    688         * @param       cls 選択タブのクラス名
    689         */
    690        public void setSelClass( final String cls ) {
    691                selClass = nval( getRequestParameter( cls ), selClass );
    692        }
    693
    694        /**
    695         * 【TAG】非選択タブのクラスを指定します(初期値:unselTab)。
    696         *
    697         * @og.tag
    698         * タブが選択されていない状態にある場合の、タブ部分のクラス名を指定します。
    699         * このクラス名を変更する場合は、そのクラスをcustom/custom.css等で再定義して下さい。
    700         * 初期値は、unselTabです。
    701         *
    702         * @param       cls 選択タブのクラス名
    703         */
    704        public void setUnselClass( final String cls ) {
    705                unselClass = nval( getRequestParameter( cls ), unselClass );
    706        }
    707
    708        /**
    709         * 【TAG】タブの方向[Horizontal(or H):横型/Vertical(or V):縦型]を指定します(初期値:Horizontal:横型)。
    710         *
    711         * @og.tag
    712         * タブは、上にタブが並ぶ横型と左にタブが並ぶ縦型があります。
    713         * この属性では、横型は、Horizontal 、縦型は、Vertical を指定します。
    714         * 指定は、文字列の最初の一文字を見ているだけですので、HかVでも構いません。
    715         *
    716         * 縦型(Vertical)にした場合、各タブ要素は、フレームサイズの幅に合わせて
    717         * 最大で表示されます。幅を固定する場合は、width属性を指定して下さい。
    718         *
    719         * 初期値は、横型(Horizontal) です。
    720         *
    721         * @param       ori タブの方向 [Horizontal(or H):横型/Vertical(or V):縦型]
    722         */
    723        public void setOrientation( final String ori ) {
    724                final String ori2 = nval( getRequestParameter( ori ),null );
    725                if( ori2 != null && ori2.length() > 0 ) {
    726                        final char ch = ori2.toUpperCase(Locale.JAPAN).charAt(0);
    727                        if( ch == 'H' ) { isHorizontal = true; }
    728                        else if( ch == 'V' ) { isHorizontal = false; }
    729                        else {
    730                                final String errMsg = "orientation の指定は、H(orizontal) または、V(ertical) です。"
    731                                                        + " orientation=" + ori2 ;                      // 5.1.8.0 (2010/07/01) errMsg 修正
    732                                throw new HybsSystemException( errMsg );
    733                        }
    734                }
    735        }
    736
    737        /**
    738         * 【TAG】タブリンクの幅を % 、px 、または "auto" で指定します(初期値:auto)。
    739         *
    740         * @og.tag
    741         * 初期値は、"auto"(自動設定) です。
    742         * autoの場合、横型表示では、文字の幅に合わせて自動的に調整され、
    743         * 縦型表示の場合は、フレームサイズに合わせて拡大して表示されます。
    744         *
    745         * @param       wh      幅 (% 、px 、または "auto" )
    746         */
    747        public void setWidth( final String wh ) {
    748                width = nval( getRequestParameter( wh ),width );
    749        }
    750
    751        /**
    752         * 【TAG】タブの高さを、% 、px 、または "auto" で指定します(初期値:auto)。
    753         *
    754         * @og.tag
    755         * タブの高さを、% 、px 、または "auto" で指定します
    756         * 初期値は、"auto"(自動設定) です。
    757         *
    758         * @param       ht      高さ (% 、px 、または "auto" )
    759         */
    760        public void setHeight( final String ht ) {
    761                height = nval( getRequestParameter( ht ),height );
    762        }
    763
    764        /**
    765         * 【HTML】JavaScriptのイベント onClick を設定します(初期値：:onClick="changeTab( this, 'selTab' );")。
    766         *
    767         * @og.tag
    768         * onClick をセットします。
    769         * 初期値は、選択タブをクリックしたタブの色を新しく選択されたタブの色と入れ替えます。
    770         * ゼロ文字列を指定すると、onClick を何も指定しないことになります。
    771         * 初期値：:onClick="changeTab( this, 'selTab' );"
    772         *
    773         * @og.rev 7.4.1.0 (2021/04/23) タブをクリックした際のJavaScriptを外部から設定できるようにします。
    774         *
    775         * @param       scrpt   onClickイベント
    776         */
    777        public void setOnClick( final String scrpt ) {
    778                // ゼロ文字列を指定すると、null になります。
    779                onClick = nval( getRequestParameter( scrpt ),null );
    780        }
    781
    782        /**
    783         * シリアライズ用のカスタムシリアライズ読み込みメソッド
    784         *
    785         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
    786         *
    787         * @og.rev 5.1.8.0 (2010/07/01) tabData の初期化処理 追加
    788         * @serialData 一部のオブジェクトは、シリアライズされません。
    789         *
    790         * @param       strm    ObjectInputStreamオブジェクト
    791         * @see #release2()
    792         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
    793         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
    794         */
    795        private void readObject( final ObjectInputStream strm ) throws IOException, ClassNotFoundException {
    796                strm.defaultReadObject();
    797                tabData         = new ArrayList<>();
    798        }
    799
    800        /**
    801         * このオブジェクトの文字列表現を返します。
    802         * 基本的にデバッグ目的に使用します。
    803         *
    804         * @return このクラスの文字列表現
    805         * @og.rtnNotNull
    806         */
    807        @Override
    808        public String toString() {
    809                return ToString.title(this.getClass().getName() )
    810                .println( "VERSION"       , VERSION )
    811                .println( "listType"      , type.toString() )
    812                .println( "href"          , href )
    813                .println( "target"        , target )
    814                .println( "openTab"       , openTab )
    815                .println( "openTabName"   , openTabName )
    816                .println( "constKeys"     , constKeys )
    817                .println( "constVals"     , constVals )
    818                .println( "listCount"     , listCount )
    819                .println( "selClass"      , selClass )
    820                .println( "unselClass"    , unselClass )
    821                .println( "isHorizontal"  , isHorizontal )
    822                .println( "width"         , width )
    823                .println( "height"        , height )
    824                .println( "Other...", getAttributes().getAttribute() ).fixForm().toString();
    825        }
    826}