    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;
    025//import org.opengion.hayabusa.common.HybsSystem;                                                               // 8.5.2.0 (2023/07/14) Delete
    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;
    059 *
    060 * ただし、値なしのOptionを含めても(addNoValue="true")、HTML5の仕様上、
    061 * 値なしのOptionは表示されません。
    062 *
    063 * @og.group 【HTML5】選択ﾃﾞｰﾀ制御
    064 * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    065 *
    066 * @version     6.0
    067 * @author      Kazuhiko Hasegawa
    068 * @since       JDK5.0,
    069 */
    070public class DatalistTag extends CommonTagSupport implements OptionAncestorIF {
    071        /** このﾌﾟﾛｸﾞﾗﾑのVERSION文字列を設定します。 {@value} */
    072        private static final String VERSION = "8.5.2.0 (2023/07/14)" ;
    073        private static final long serialVersionUID = 852020230714L ;
    074
    075        private transient Options option        = new Options();
    076        /** ﾌｫｰﾑと関連付けるid */
    077        private String id;
    078
    079        /**
    080         * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
    081         *
    082         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    083         */
    084        public DatalistTag() { super(); }               // これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。
    085
    086        /**
    087         * Taglibの開始ﾀｸﾞが見つかったときに処理する doStartTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    088         *
    089         * @return      後続処理の指示( EVAL_BODY_INCLUDE )
    090         */
    091        @Override
    092        public int doStartTag() {
    093                // 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
    094                return useTag()
    095                                        ? EVAL_BODY_INCLUDE             // Body ｲﾝｸﾙｰﾄﾞ( extends TagSupport 時)
    096                                        : SKIP_BODY ;                   // Body を評価しない
    097        }
    098
    099        /**
    100         * Taglibの終了ﾀｸﾞが見つかったときに処理する doEndTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    101         *
    102         * @og.rev 5.7.6.2 (2014/05/16) IEのHTML5機能が無効の場合、INDBMENU を作成します。
    103         * @og.rev 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
    104         * @og.rev 8.5.2.0 (2023/07/14) 一部の機能廃止による修正(問合・ﾄﾗﾌﾞﾙ 0200010980)
    105         *
    106         * @return      後続処理
    107         */
    108        @Override
    109        public int doEndTag() {
    110                debugPrint();                                   // 4.0.0 (2005/02/28)
    111                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
    112                if( useTag() ) {
    113                        // 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
    114                        if( id == null ) {
    115                                final String errMsg = "idは必須です。" ;
    116                                throw new OgRuntimeException( errMsg );
    117                        }
    118
    119                        final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
    120
    121//                      // 5.7.6.2 (2014/05/16) IEのHTML5機能が無効の場合の処理 8.5.2.0 (2023/07/14) Delete
    122//                      final String ieHTML5 = (String)getSessionAttribute( HybsSystem.IE_HTML5_KEY );
    123//                      if( "FALSE".equalsIgnoreCase( ieHTML5 ) ) {
    124//                              final String inName = id.endsWith( ".sel" ) ? id.substring( 0,id.length()-4 ) : id ;
    125//
    126//                              rtn.append("<select id='").append( id )
    127//                                      .append( "' style='position:absolute;' onChange='selChanged(this);' >" )
    128//                                      .append( option.getOption() )
    129//                                      // 8.1.0.0 (2021/12/28) HTML5 準拠に見直し(<script> type属性削除)
    130////                                    .append( "</select><script type='text/javascript'>makeInputMenu('" )
    131//                                      .append( "</select><script>makeInputMenu('" )
    132//                                      .append( inName ).append( "');</script>" );
    133//                      }
    134//                      else {
    135//                              // display:none は、datalist の optionのBODY部が、HTML5 以外では表示されてしまうため。
    136//                              rtn.append("<div style='display:none;'><datalist id='")
    137//                                      .append( id ).append( "' >" )
    138//                                      .append( option.getOption() )
    139//                                      .append( "</datalist></div>" );
    140//                      }
    141                        rtn.append("<datalist id='")
    142                                .append( id ).append( "' >" )
    143                                .append( option.getOption() )
    144                                .append( "</datalist>" );
    145
    146                        jspPrint( rtn.toString() );
    147                }
    148                return EVAL_PAGE ;
    149        }
    150
    151        /**
    152         * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします。
    153         * ｷｬｯｼｭされて再利用されるので、ﾌｨｰﾙﾄﾞの初期設定を行います。
    154         *
    155         */
    156        @Override
    157        protected void release2() {
    158                super.release2();
    159                option  = new Options();
    160                id              = null;
    161        }
    162
    163        /**
    164         * ﾃﾞｰﾀﾘｽﾄの選択項目を追加します。
    165         *
    166         * datalist ﾀｸﾞのBODY要素の OptionTag よりｱｸｾｽされます。
    167         *
    168         * @param       opt     ｵﾌﾟｼｮﾝﾀｸﾞ文字列
    169         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    170         */
    171        @Override       // OptionAncestorIF
    172        public void addOption( final String opt ) {
    173                option.add( opt );
    174        }
    175
    176        /**
    177         * ﾒﾆｭｰ項目の最後の項目を削除します。
    178         *
    179         * select ﾀｸﾞのBODY要素の OptionTag よりｱｸｾｽされます。
    180         *
    181         * @og.rev 6.8.0.0 (2017/06/02) ﾒﾆｭｰ項目の最後の項目を削除。
    182         *
    183         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#removeLast()
    184         */
    185        @Override       // OptionAncestorIF
    186        public void removeLast() {
    187                option.removeLast();
    188        }
    189
    190        /**
    191         * 【HTML】要素に対して固有の名前(id)をつける場合に設定します。
    192         *
    193         * @og.tag
    194         * ﾃﾞｰﾀﾘｽﾄ の id 属性は、ﾌｫｰﾑの list 属性と同じｷｰを指定する事で関連付けします。
    195         *
    196         * ※
    197         * 内部事情で、inputﾀｸﾞ(columnﾀｸﾞ)の list属性 に設定するｷｰも、id属性に設定するｷｰも、
    198         * inputﾀｸﾞ(columnﾀｸﾞ)の name属性＋".sel" を標準的に使用してください。
    199         *
    200         * @param       id      固有の名前
    201         */
    202        @Override
    203        public void setId( final String id ) {
    204                this.id = nval( getRequestParameter( id ), null );
    205        }
    206
    207        /**
    208         * 値を外部から取り出します。
    209         *
    210         * OptionTag で、value を取り出して、内部の値と同じ場合は、選択状態にします。
    211         *
    212         * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    213         *
    214         * @return      null固定
    215         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    216         */
    217        @Override       // OptionAncestorIF
    218        public String getValue() {
    219                // ここでは、何もしません。
    220                return null;
    221        }
    222
    223        /**
    224         * 複数選択可能時に全選択を設定するかどうかを返します。
    225         *
    226         * これは、上位入れ子のﾀｸﾞの OptionTag で、multipleAll を取り出して、
    227         * true であれば、全選択に設定します。
    228         *
    229         * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    230         *
    231         * @return      false固定
    232         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    233         */
    234        @Override       // OptionAncestorIF
    235        public boolean isMultipleAll() {
    236                // ここでは、何もしません。
    237                return false;
    238        }
    239
    240        /**
    241         * ﾊﾟﾗﾒｰﾀｰ変換({&#064;XXXX}の置き換えをしない状態のﾊﾟﾗﾒｰﾀｰをｾｯﾄします。
    242         *
    243         * ※ ここでは、何もしません。
    244         *
    245         * @og.rev 5.7.1.0 (2013/12/06) 新規追加
    246         *
    247         * @param       param   ﾊﾟﾗﾒｰﾀｰ
    248         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#addOption( String )
    249         */
    250        @Override       // OptionAncestorIF
    251        public void setRawParam( final String param ) {
    252                // ここでは、何もしません。
    253        }
    254
    255        /**
    256         * ｾﾚｸﾄﾒﾆｭｰの場合、ｷｰ:ﾗﾍﾞﾙ形式で表示するかどうか[true/false/null]を返します。
    257         *
    258         * これは、上位入れ子のﾀｸﾞの OptionTag で、addKeyLabel を取り出して、
    259         * true であれば、ｷｰ:ﾗﾍﾞﾙ形式 のｵﾌﾟｼｮﾝを、#addOption( String ) で
    260         * 登録させます。
    261         *
    262         * @og.rev 6.0.4.0 (2014/11/28) ｷｰ:ﾗﾍﾞﾙ形式で表示するかどうか。新規追加
    263         *
    264         * @return      null固定
    265         * @see         #addOption( String )
    266         * @see         org.opengion.hayabusa.taglib.OptionAncestorIF#getAddKeyLabel()
    267         */
    268        @Override       // OptionAncestorIF
    269        public String getAddKeyLabel() {
    270                // ここでは、何もしません。
    271                return null;
    272        }
    273
    274        /**
    275         * ｼﾘｱﾗｲｽﾞ用のｶｽﾀﾑｼﾘｱﾗｲｽﾞ読み込みﾒｿｯﾄﾞ
    276         *
    277         * ここでは、transient 宣言された内部変数の内、初期化が必要なﾌｨｰﾙﾄﾞのみ設定します。
    278         *
    279         * @serialData 一部のｵﾌﾞｼﾞｪｸﾄは、ｼﾘｱﾗｲｽﾞされません。
    280         *
    281         * @param       strm    ObjectInputStreamｵﾌﾞｼﾞｪｸﾄ
    282         * @see         #release2()
    283         * @throws      IOException     ｼﾘｱﾗｲｽﾞに関する入出力ｴﾗｰが発生した場合
    284         * @throws      ClassNotFoundException  ｸﾗｽを見つけることができなかった場合
    285         */
    286        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
    287                strm.defaultReadObject();
    288                option = new Options();
    289        }
    290
    291        /**
    292         * このｵﾌﾞｼﾞｪｸﾄの文字列表現を返します。
    293         * 基本的にﾃﾞﾊﾞｯｸﾞ目的に使用します。
    294         *
    295         * @return      このｸﾗｽの文字列表現
    296         * @og.rtnNotNull
    297         */
    298        @Override
    299        public String toString() {
    300                return ToString.title( this.getClass().getName() )
    301                                .println( "VERSION"             ,VERSION                )
    302                                .println( "id"                  ,id                             )
    303                                .println( "Other..."    ,getAttributes().getAttribute() )
    304                                .fixForm().toString() ;
    305        }
    306}