    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.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import static org.opengion.fukurou.util.StringUtil.nval;
    020
    021import java.io.IOException;
    022import java.io.ObjectInputStream;
    023
    024import org.opengion.fukurou.util.Options;
    025import org.opengion.hayabusa.common.HybsSystem;
    026import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
    027
    028/**
    029 * フォームの入力欄などで入力候補となるデータリストを定義するHTML拡張タグです。
    030 * HTML5 から、新たに追加された要素です。
    031 *
    032 * データリスト内の選択肢は、optionタグ、queryOptionタグによって指定します。
    033 * データリスト の id 属性は、フォームの list 属性と同じキーを指定する事で関連付けします。
    034 *
    035 * @og.formSample
    036 * ●形式：&lt;og:datalist id="…" /&gt;
    037 * ●body：あり(EVAL_BODY_INCLUDE:BODYをインクルードし、{&#064;XXXX} は解析しません)
    038 *
    039 * ●Tag定義：
    040 *   &lt;og:datalist
    041 *       id               ○【TAG】入力候補を表示するフォームの list 属性に設定する id (必須)
    042 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
    043 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
    044 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
    045 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
    046 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
    047 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    048 *   &gt;   ... Body ...
    049 *   &lt;/og:datalist&gt;
    050 *
    051 * ●使用例
    052 * &lt;og:input type="text" name="tokyo" autocomplete="on" list="tokyo.sel" /&gt;
    053 *
    054 *  &lt;og:datalist id="tokyo.sel" &gt;
    055 *      &lt;og:option value="渋谷" /&gt;
    056 *      &lt;og:option value="新宿" /&gt;
    057 *      &lt;og:option value="池袋" /&gt;
    058 *  &lt;/og:datalist&gt;&lt;
    059 *
    060 * @og.group 【HTML5】選択データ制御
    061 * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    062 *
    063 * @version  6.0
    064 * @author       Kazuhiko Hasegawa
    065 * @since    JDK5.0,
    066 */
    067public class DatalistTag extends CommonTagSupport implements OptionAncestorIF {
    068        /** このプログラムのVERSION文字列を設定します。   {@value} */
    069        private static final String VERSION = "6.8.0.0 (2017/06/02)" ;
    070        private static final long serialVersionUID = 680020170602L ;
    071
    072        private transient Options option        = new Options();
    073
    074        private String id       ;               // フォームと関連付けるid
    075
    076        /**
    077         * デフォルトコンストラクター
    078         *
    079         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    080         */
    081        public DatalistTag() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    082
    083        /**
    084         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
    085         *
    086         * @return      後続処理の指示( EVAL_BODY_INCLUDE )
    087         */
    088        @Override
    089        public int doStartTag() {
    090                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
    091                return useTag()
    092                                        ? EVAL_BODY_INCLUDE             // Body インクルード( extends TagSupport 時)
    093                                        : SKIP_BODY ;                   // Body を評価しない
    094
    095        }
    096
    097        /**
    098         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    099         *
    100         * @og.rev 5.7.6.2 (2014/05/16) IEのHTML5機能が無効の場合、INDBMENU を作成します。
    101         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
    102         *
    103         * @return      後続処理
    104         */
    105        @Override
    106        public int doEndTag() {
    107                debugPrint();           // 4.0.0 (2005/02/28)
    108                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
    109                if( useTag() ) {
    110                        // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
    111                        if( id == null ) {
    112                                final String errMsg = "idは必須です。" ;
    113                                throw new OgRuntimeException( errMsg );
    114                        }
    115
    116                        final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
    117
    118                        // 5.7.6.2 (2014/05/16) IEのHTML5機能が無効の場合の処理
    119                        final String ieHTML5 = (String)getSessionAttribute( HybsSystem.IE_HTML5_KEY );
    120                        if( "FALSE".equalsIgnoreCase( ieHTML5 ) ) {
    121                                final String inName = id.endsWith( ".sel" ) ? id.substring( 0,id.length()-4 ) : id ;
    122
    123                                rtn.append("<select id='").append( id )
    124                                        .append( "' style='position:absolute;' onChange='selChanged(this);' >" )
    125                                        .append( option.getOption() )
    126                                        .append( "</select><script type='text/javascript'>makeInputMenu('" )
    127                                        .append( inName ).append( "');</script>" );
    128                        }
    129                        else {
    130                                // display:none は、datalist の optionのBODY部が、HTML5 以外では表示されてしまうため。
    131                                rtn.append("<div style='display:none;'><datalist id='")
    132                                        .append( id ).append( "' >" )
    133                                        .append( option.getOption() )
    134                                        .append( "</datalist></div>" );
    135                        }
    136
    137                        jspPrint( rtn.toString() );
    138                }
    139                return EVAL_PAGE ;
    140        }
    141
    142        /**
    143         * タグリブオブジェクトをリリースします。
    144         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    145         *
    146         */
    147        @Override
    148        protected void release2() {
    149                super.release2();
    150                option  = new Options();
    151                id              = null;
    152        }
    153
    154        /**
    155         * データリストの選択項目を追加します。
    156         *
    157         * datalist タグのBODY要素の OptionTag よりアクセスされます。
    158         *
    159         * @param       opt      オプションタグ文字列
    160         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    161         */
    162        public void addOption( final String opt ) {
    163                option.add( opt );
    164        }
    165
    166        /**
    167         * メニュー項目の最後の項目を削除します。
    168         *
    169         * select タグのBODY要素の OptionTag よりアクセスされます。
    170         *
    171         * @og.rev 6.8.0.0 (2017/06/02) メニュー項目の最後の項目を削除。
    172         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#removeLast()
    173         */
    174        public void removeLast() {
    175                option.removeLast();
    176        }
    177
    178        /**
    179         * 【HTML】要素に対して固有の名前(id)をつける場合に設定します。
    180         *
    181         * @og.tag
    182         * データリスト の id 属性は、フォームの list 属性と同じキーを指定する事で関連付けします。
    183         *
    184         * ※
    185         * 内部事情で、inputタグ(columnタグ)の list属性 に設定するキーも、id属性に設定するキーも、
    186         * inputタグ(columnタグ)の name属性＋".sel" を標準的に使用してください。
    187         *
    188         * @param   id 固有の名前
    189         */
    190        @Override
    191        public void setId( final String id ) {
    192                this.id = nval( getRequestParameter( id ), null );
    193        }
    194
    195        /**
    196         * 値を外部から取り出します。
    197         *
    198         * OptionTag で、value を取り出して、内部の値と同じ場合は、選択状態にします。
    199         *
    200         * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    201         *
    202         * @return      null固定
    203         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    204         */
    205        public String getValue() {
    206                // ここでは、何もしません。
    207                return null;
    208        }
    209
    210        /**
    211         * 複数選択可能時に全選択を設定するかどうかを返します。
    212         *
    213         * これは、上位入れ子のタグの OptionTag で、multipleAll を取り出して、
    214         * true であれば、全選択に設定します。
    215         *
    216         * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    217         *
    218         * @return      false固定
    219         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    220         */
    221        public boolean isMultipleAll() {
    222                // ここでは、何もしません。
    223                return false;
    224        }
    225
    226        /**
    227         * セレクトメニューの場合、キー:ラベル形式で表示するかどうか[true/false/null]を返します。
    228         *
    229         * これは、上位入れ子のタグの OptionTag で、addKeyLabel を取り出して、
    230         * true であれば、キー:ラベル形式 のオプションを、#addOption( String ) で
    231         * 登録させます。
    232         *
    233         * @og.rev 6.0.4.0 (2014/11/28) キー:ラベル形式で表示するかどうか。新規追加
    234         *
    235         * @return      null固定
    236         * @see         #addOption( String )
    237         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#getAddKeyLabel()
    238         */
    239        public String getAddKeyLabel() {
    240                // ここでは、何もしません。
    241                return null;
    242        }
    243
    244        /**
    245         * パラメーター変換({&#064;XXXX}の置き換えをしない状態のパラメーターをセットします。
    246         *
    247         * ※ ここでは、何もしません。
    248         *
    249         * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    250         *
    251         * @param   param パラメーター
    252         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    253         */
    254        public void setRawParam( final String param ) {
    255                // ここでは、何もしません。
    256        }
    257
    258        /**
    259         * シリアライズ用のカスタムシリアライズ読み込みメソッド
    260         *
    261         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
    262         *
    263         * @serialData 一部のオブジェクトは、シリアライズされません。
    264         *
    265         * @param       strm    ObjectInputStreamオブジェクト
    266         * @see #release2()
    267         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
    268         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
    269         */
    270        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
    271                strm.defaultReadObject();
    272                option = new Options();
    273        }
    274
    275        /**
    276         * このオブジェクトの文字列表現を返します。
    277         * 基本的にデバッグ目的に使用します。
    278         *
    279         * @return このクラスの文字列表現
    280         * @og.rtnNotNull
    281         */
    282        @Override
    283        public String toString() {
    284                return ToString.title( this.getClass().getName() )
    285                                .println( "VERSION"             ,VERSION                )
    286                                .println( "id"                  ,id                             )
    287                                .println( "Other..."    ,getAttributes().getAttribute() )
    288                                .fixForm().toString() ;
    289        }
    290}