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 * ●形式:<og:datalist id="…" />
037 * ●body:あり(EVAL_BODY_INCLUDE:BODYをインクルードし、{@XXXX} は解析しません)
038 *
039 * ●Tag定義:
040 *   <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 *   >   ... Body ...
049 *   </og:datalist>
050 *
051 * ●使用例
052 * <og:input type="text" name="tokyo" autocomplete="on" list="tokyo.sel" />
053 *
054 *  <og:datalist id="tokyo.sel" >
055 *      <og:option value="渋谷" />
056 *      <og:option value="新宿" />
057 *      <og:option value="池袋" />
058 *  </og:datalist>
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}