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.plugin.column;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.hayabusa.db.AbstractEditor;
021import org.opengion.hayabusa.db.CellEditor;
022import org.opengion.hayabusa.db.DBColumn;
023import org.opengion.hayabusa.db.SelectionCellEditor;                                                    // 6.2.2.0 (2015/03/27)
024import org.opengion.hayabusa.db.Selection;
025import org.opengion.hayabusa.db.SelectionFactory;
026import org.opengion.fukurou.util.StringFormat;
027import org.opengion.fukurou.util.XHTMLTag;
028import org.opengion.fukurou.util.TagBuffer;
029
030import static org.opengion.fukurou.util.StringUtil.isNull;                                              // 6.1.1.0 (2015/01/17)
031
032/**
033 * カラムの編集パラメーターのSQL文の実行結果より、datalistを作成して
034 * 入力候補となるデータリストを定義する編集用エディタークラスです。
035 * datalist は、HTML5 から採用されたタグです。
036 *
037 * 編集パラメータには、datalistを作成するための、SQL文を記述します。
038 * このSQL文は、select KEY,LABEL from xx ・・・ という構文で、KEY部分とLABEL部分が
039 * 選択されます。
040 * datalist 自身が、HTML5からの新機能なので、現時点では、これ以上の機能はありません。
041 * 将来的に、DBMENU などと同様に、第三カラム以降を利用可能になると思いますので、
042 * 今は使わないでください。(将来の機能追加時に互換性問題を引き起こすかもしれませんので)
043 *
044 * 入力フィールドとdatalistタグとの関係付は、カラムIDに、"カラムID.sel" で結びつけます。
045 *
046 * ただし、値なしのOptionを含めても(addNoValue="true")、HTML5の仕様上、
047 * 値なしのOptionは表示されません。
048 *
049 * <input name="カラムID" list="カラムID.sel" />
050 * <del>&lt;div style="display:none;"&gt;</del>
051 *   &lt;datalist id="カラムID.sel"&gt;
052 *     &lt;option value="KEY1"&gt;LABEL1&lt;/option&gt;
053 *     &lt;option value="KEY2"&gt;LABEL2&lt;/option&gt;
054 *     &lt;option value="KEY3"&gt;LABEL3&lt;/option&gt;
055 *   &lt;/datalist&gt;
056 * <del>&lt;/div&gt;</del>
057 *
058 * 7.0.1.4 (2018/11/26) autocomplete="off" を初期設定します。
059 *
060 * <del>divタグは、HTML5 非対応ブラウザを使用した場合、datalist の option がそのまま
061 * テキストとして見えてしまうのを避けるためです。</del>
062 *
063 * 一覧表出力時の getValue( int ,String ) 処理では、Selection オブジェクトの
064 * キャッシュ機能を利用して、同一Selection オブジェクトの間は、datalist は、
065 * 1度しか、出力しない様に制御しています。これにより、共有のdatalist を使用する為、
066 * HTMLの出力データ量を抑えることが可能になります。
067 * (キャッシュを利用しないと100行出力すると100個のdatalistを出力する事になります。)
068 * <del>(同様の機能を持つ INDBMENU では、行ごとにプルダウンデータを作成しています。)</del>
069 * ただし、行単位にSQLの条件を変える機能(AAA:BBB:CCC:DDD引数)が指定された場合は、
070 * 行ごとに出力します。
071 *
072 * 各カラムの値(value値)に、AAA:BBB:CCC:DDD という値を設定できます。これは、
073 * $1,$2,$3,$4 に割り当てなおして、QUERYを実行します。また、$1 は、本来の値として、
074 * メニューの初期値設定等に使用します。上記の例では、AAA が値で、それ以降は、
075 * 引数になります。
076 * 又、$Cには自分自身のカラム名を割り当てます。
077 * この機能を使用すれば、動的メニューを行ごとに条件を変えて作成することが
078 * 可能になります。
079 * 例:select KEY,LABEL from xx where KUBUN='$2' and CDK='$3'
080 * さらに、元の文字列"AAA:BBB:CCC:DDD"は、$0 に割り当てられます。割り当てがない
081 * 変数は、""(ゼロ文字列)として、扱われます。
082 *
083 * カラムの表示に必要な属性は、DBColumn オブジェクト より取り出します。
084 * このクラスは、DBColumn オブジェクト毎に1つ作成されます。
085 *
086 * @og.rev 5.7.4.3 (2014/03/28) 新規作成
087 * @og.rev 6.2.2.0 (2015/03/27) SelectionCellEditor I/Fを追加
088 * @og.rev 8.5.2.0 (2023/07/14) 一部の機能廃止による修正(問合・トラブル 0200010980)
089 * @og.group データ編集(HTML5)
090 *
091 * @version     4.0
092 * @author      Kazuhiko Hasegawa
093 * @since       JDK5.0,
094 */
095public class Editor_DATALIST extends AbstractEditor implements SelectionCellEditor {
096        /** このプログラムのVERSION文字列を設定します。 {@value} */
097        private static final String VERSION = "8.5.2.0 (2023/07/14)" ;
098
099        // 5.7.5.0 (2014/04/04) datalist 使用時は、display:none にして、HTML5未対応のブラウザに備える。
100        private static final String DIV1 = "<div style=\"display:none;\">" ;
101        private static final String DIV2 = "</div>" ;
102        private static final String AUTO = "autocomplete" ;             // 7.0.1.4 (2018/11/26)
103        private static final String OFF  = "off" ;                              // 7.0.1.4 (2018/11/26)
104
105        private final String query      ;
106        private final String dbid       ;
107        private final String lang       ;
108        private final String useSLabel;                                                 // 6.2.0.0 (2015/02/27) SLABEL 対応
109        private final String addKeyLabel;                                               // 6.2.0.0 (2015/02/27) キー:ラベル形式
110        /** データのコード情報 */
111        private final Selection selection;                                              // 8.5.2.0 (2023/07/14) Add
112        /** シーケンスフラグ */
113        private final boolean seqFlag;                                                  // 8.5.2.0 (2023/07/14) Add
114
115        private Selection bkSel         ;                                                       // 5.7.5.0 (2014/04/04) Selection オブジェクトのキャッシュ機能
116
117        /**
118         * デフォルトコンストラクター。
119         * このコンストラクターで、基本オブジェクトを作成します。
120         *
121         * @og.rev 6.2.0.0 (2015/02/27) SLABEL 対応
122         * @og.rev 6.2.0.0 (2015/02/27) キー:ラベル形式で表示するかどうかを、指定できるようにします。
123         * @og.rev 8.5.2.0 (2023/07/14) 一部の機能廃止による修正(問合・トラブル 0200010980)
124         */
125        public Editor_DATALIST() {
126                super();                                                // 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
127                query           = null;
128                dbid            = null;
129                lang            = null;
130                useSLabel       = "auto";                                                               // 6.2.0.0 (2015/02/27) SLABEL 対応
131                addKeyLabel     = null;                                                                 // 6.2.0.0 (2015/02/27) キー:ラベル形式
132                selection       = null;                                                                 // 8.5.2.0 (2023/07/14) Add
133                seqFlag         = false;                                                                // 8.5.2.0 (2023/07/14) Add
134        }
135
136        /**
137         * DBColumnオブジェクトを指定したprivateコンストラクター。
138         *
139         * @og.rev 6.2.0.0 (2015/02/27) SLABEL 対応
140         * @og.rev 6.2.0.0 (2015/02/27) キー:ラベル形式で表示するかどうかを、指定できるようにします。
141         * @og.rev 7.0.1.4 (2018/11/26) autocomplete="off" を初期設定します。
142         * @og.rev 8.5.2.0 (2023/07/14) 一部の機能廃止による修正(問合・トラブル 0200010980)
143         *
144         * @param       clm     DBColumnオブジェクト
145         */
146        private Editor_DATALIST( final DBColumn clm ) {
147                super( clm );
148                // 7.0.1.4 (2018/11/26) autocomplete="off" を初期設定します。
149                if( isNull( attributes.get( AUTO ) ) ) { attributes.set( AUTO,OFF ); }
150
151                tagBuffer.add( XHTMLTag.inputAttri( attributes ) );
152
153                query           = clm.getEditorParam();
154                dbid            = clm.getDbid();
155                lang            = clm.getLang();                                                // 4.0.0.0 (2006/11/15)
156                useSLabel       = clm.getUseSLabel();                                   // 6.2.0.0 (2015/02/27) SLABEL 対応
157                addKeyLabel     = clm.getAddKeyLabel();                                 // 6.2.0.0 (2015/02/27) キー:ラベル形式
158                seqFlag         = "SEQ".equals( query );                                // 8.5.2.0 (2023/07/14) Add
159
160                // 編集パラメータが未指定の場合は、データのコード情報を取得 8.5.2.0 (2023/07/14) Add
161                if( query == null || query.isEmpty() || seqFlag ) {
162                        selection = SelectionFactory.newSelection( "MENU",clm.getCodeData(),addKeyLabel );
163                }
164                else {
165                        selection = null;
166                }
167
168                // 3.5.5.9 (2004/06/07)
169//              if( query == null || query.isEmpty() ) {
170                // 編集パラメータ 又は データのコード が未指定の場合 8.5.2.0 (2023/07/14) Modify
171                if( ( query == null || query.isEmpty() ) && selection == null ) {
172                        final String errMsg = "DATALIST Editor では、編集パラメータは必須です。"
173                                        + " name=[" + name + "]" + CR ;
174                        throw new HybsSystemException( errMsg );
175                }
176        }
177
178        /**
179         * 各オブジェクトから自分のインスタンスを返します。
180         * 自分自身をキャッシュするのか、新たに作成するのかは、各サブクラスの実装に
181         * まかされます。
182         *
183         * @param       clm     DBColumnオブジェクト
184         * @return      CellEditorオブジェクト
185         * @og.rtnNotNull
186         */
187        public CellEditor newInstance( final DBColumn clm ) {
188                return new Editor_DATALIST( clm );
189        }
190
191        /**
192         * データの編集用文字列を返します。
193         *
194         * ここでは、AAA:BBB:CCC:DDD という値を、$1,$2,$3,$4 に割り当てなおして、
195         * QUERYを実行します。また、$1 は、本来の値として、メニューの初期値設定等に
196         * 使用します。上記の例では、AAA が値で、それ以降は、引数になります。
197         * さらに、元の文字列"AAA:BBB:CCC:DDD"は、$0 に割り当てられます。割り当てがない
198         * 変数は、""(ゼロ文字列)として、扱われます。
199         * 又、$Cには自分自身のカラム名を割り当てます。
200         *
201         * @og.rev 5.7.5.0 (2014/04/04) datalist 使用時は、display:none にして、HTML5未対応のブラウザに備える。
202         * @og.rev 5.7.6.2 (2014/05/16) list属性とid属性の結びつきを、name+".sel" に変更
203         * @og.rev 6.2.0.0 (2015/02/27) SLABEL 対応
204         * @og.rev 6.4.5.3 (2016/05/13) value は、コロン区切りの先頭だけ分離する。
205         * @og.rev 8.5.2.0 (2023/07/14) 一部の機能廃止による修正(問合・トラブル 0200010980)
206         *
207         * @param       value   入力値
208         * @return      データの編集用文字列
209         * @og.rtnNotNull
210         */
211        @Override
212        public String getValue( final String value ) {
213                final String newValue = StringFormat.getValue( value );                         // 6.4.5.3 (2016/05/13) コロン区切りの先頭だけ
214
215                // input タグの作成
216                // 6.1.1.0 (2015/01/17) TagBufferの連結記述
217                final String intag = new TagBuffer( "input" )
218                                                .add( "name"    , name )                                                                        // 4.3.6.0 (2009/04/01)
219                                                .add( "id"              , name , isNull( attributes.get( "id" ) ) )     // 4.3.7.2 (2009/06/15)
220                                                .add( "list"    , name + ".sel" )                                                       // datalistタグとの関係付けるためのキーワード
221                                                .add( "value"   , newValue )                                                            // 6.4.5.3 (2016/05/13)
222                                                .add( "size"    , size1 )
223                                                .add( tagBuffer.makeTag() )
224                                                .makeTag();
225
226                final boolean useSlbl = "true".equalsIgnoreCase( useSLabel );                   // 6.2.0.0 (2015/02/27)
227
228                // datalist タグの作成
229                // 6.1.1.0 (2015/01/17) TagBufferの連結記述
230//              final String dltag = getOption(
231//                                      new TagBuffer( "datalist" )
232//                                              .add( "id"      , name + ".sel" )                                                       // inputタグとの関係付けるためのキーワード
233//                                      , value
234//                                      , false                                                                                                         // キャッシュは使用しない。(つまり、null は返らない)
235//                                      , useSlbl                                                                                                       // 6.2.0.0 (2015/02/27) SLABEL 対応
236//                              ).makeTag() ;
237
238                // 8.5.2.0 (2023/07/14) Modify
239                final String dltag;
240                // データのコード情報からdatalist作成
241                if( selection != null ) {
242                        dltag = new TagBuffer( "datalist" )
243                                                        .add( "id"              , name + ".sel" )                                       // inputタグとの関係付けるためのキーワード
244                                                        .addBody( selection.getOption( value,seqFlag,useSlbl ) )
245                                                        .makeTag();
246                }
247                // 編集パラメータからdatalist作成
248                else {
249                        dltag = getOption(
250                                        new TagBuffer( "datalist" )
251                                                .add( "id"      , name + ".sel" )                                                       // inputタグとの関係付けるためのキーワード
252                                        , value
253                                        , false                                                                                                         // キャッシュは使用しない。(つまり、null は返らない)
254                                        , useSlbl                                                                                                       // 6.2.0.0 (2015/02/27) SLABEL 対応
255                                ).makeTag() ;
256                }
257
258                // DIV1 の display:none は、datalist の optionのBODY部が、HTML5 以外では表示されてしまうのを防ぐため。
259                return intag + CR + DIV1 + dltag + DIV2 + CR;
260        }
261
262        /**
263         * name属性を変えた、データ表示/編集用のHTML文字列を作成します。
264         * テーブル上の name に 行番号を付加して、名前_行番号 で登録するキーを作成し、
265         * リクエスト情報を1つ毎のフィールドで処理できます。
266         *
267         * ここでは、AAA:BBB:CCC:DDD という値を、$1,$2,$3,$4 に割り当てなおして、
268         * QUERYを実行します。また、$1 は、本来の値として、メニューの初期値設定等に
269         * 使用します。上記の例では、AAA が値で、それ以降は、引数になります。
270         * さらに、元の文字列"AAA:BBB:CCC:DDD"は、$0 に割り当てられます。割り当てがない
271         * 変数は、""(ゼロ文字列)として、扱われます。
272         * 又、$Cには自分自身のカラム名を割り当てます。
273         *
274         * @og.rev 5.7.5.0 (2014/04/04) datalist 使用時は、display:none にして、HTML5未対応のブラウザに備える。
275         * @og.rev 5.7.5.0 (2014/04/04) Selection オブジェクトのキャッシュ機能
276         * @og.rev 5.7.6.2 (2014/05/16) list属性とid属性の結びつきを、name+".sel" に変更
277         * @og.rev 6.2.0.0 (2015/02/27) SLABEL 対応
278         * @og.rev 6.4.5.3 (2016/05/13) value は、コロン区切りの先頭だけ分離する。
279         * @og.rev 8.5.2.0 (2023/07/14) 一部の機能廃止による修正(問合・トラブル 0200010980)
280         *
281         * @param       row             行番号
282         * @param       value   入力値
283         * @return      データ表示/編集用の文字列
284         * @og.rtnNotNull
285         */
286        @Override
287        public String getValue( final int row,final String value ) {
288                final String name2 = name + HybsSystem.JOINT_STRING + row ;
289
290                // 5.7.5.0 (2014/04/04) Selection オブジェクトのキャッシュ機能 (true:使用可能)
291                final boolean useSelCache = value != null && value.indexOf( ':' ) < 0 ;
292
293                final String listId = useSelCache ? name : name2;                                               // キャッシュを使用する場合は、共通の name を使う。
294
295                final String newValue = StringFormat.getValue( value );                                 // 6.4.5.3 (2016/05/13) コロン区切りの先頭だけ
296
297                // input タグの作成
298                // 6.1.1.0 (2015/01/17) TagBufferの連結記述
299                final String intag = new TagBuffer( "input" )
300                                                .add( "name"    , name2 )                                                                               // 4.3.6.0 (2009/04/01)
301                                                .add( "id"              , name2 , isNull( attributes.get( "id" ) ) )    // 4.3.7.2 (2009/06/15)
302                                                .add( "list"    , listId + ".sel" )                                                             // datalistタグとの関係付けるためのキーワード
303                                                .add( "value"   , newValue )                                                                    // 6.4.5.3 (2016/05/13)
304                                                .add( "size"    , size2 )
305                                                .add( tagBuffer.makeTag() )
306                                                .makeTag( row,newValue );                                                                               // 6.4.5.3 (2016/05/13)
307
308                final boolean useSlbl = "auto".equalsIgnoreCase( useSLabel ) || "true".equalsIgnoreCase( useSLabel );   // 6.2.0.0 (2015/02/27)
309
310                // datalist タグの作成
311                // 6.1.1.0 (2015/01/17) TagBufferの連結記述
312//              final TagBuffer dltag = getOption(
313//                                      new TagBuffer( "datalist" )
314//                                              .add( "id"      , listId + ".sel" )                                                                     // inputタグとの関係付けるためのキーワード
315//                                      , value
316//                                      , useSelCache
317//                                      , useSlbl                                                                                                                       // 6.2.0.0 (2015/02/27) SLABEL 対応
318//                              );
319
320                // 8.5.2.0 (2023/07/14) Modify
321                final TagBuffer dltag;
322                // データのコード情報からdatalist作成
323                if( selection != null ) {
324                        dltag = new TagBuffer( "datalist" )
325                                                        .add( "id"              , name + ".sel" )                                                       // datalistタグとの関係付けるためのキーワード
326                                                        .addBody( selection.getOption( value,seqFlag,useSlbl ) );
327                }
328                // 編集パラメータからdatalist作成
329                else {
330                        dltag = getOption(
331                                        new TagBuffer( "datalist" )
332                                                .add( "id"      , listId + ".sel" )                                                                     // datalistタグとの関係付けるためのキーワード
333                                        , value
334                                        , useSelCache
335                                        , useSlbl                                                                                                                       // 6.2.0.0 (2015/02/27) SLABEL 対応
336                                );
337
338                }
339
340                // キャッシュが効くと、getOption の戻り値は、null になる。
341                // 6.1.1.0 (2015/01/17) TagBufferの連結記述
342                // 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
343                // 反転注意
344                return dltag == null
345                                        ? intag + CR
346                                        : intag + CR + DIV1 + dltag.makeTag( row,newValue ) + DIV2 + CR ;               // 6.4.5.3 (2016/05/13)
347        }
348
349        /**
350         * 初期値が選択済みの 選択肢(オプション)をTagBuffer に反映します。
351         * このオプションは、引数の値を初期値とするオプションタグ作成し、TagBuffer
352         * に値を設定して返します。
353         *
354         * 第3引数は、Selection オブジェクトのキャッシュ機能を使用するかどうか指定します。
355         * true で、使用する事を前提に、チェックを行います。
356         * DBMENU など、他のメソッドでは、ラベル(短)の使用有無として使用しているため、異なります。
357         *
358         * ここでは、AAA:BBB:CCC:DDD という値を、$1,$2,$3,$4 に割り当てなおして、
359         * QUERYを実行します。また、$1 は、本来の値として、メニューの初期値設定等に
360         * 使用します。上記の例では、AAA が値で、それ以降は、引数になります。
361         * さらに、元の文字列"AAA:BBB:CCC:DDD"は、$0 に割り当てられます。割り当てがない
362         * 変数は、""(ゼロ文字列)として、扱われます。
363         * 又、$Cには自分自身のカラム名を割り当てます。
364         *
365         * @og.rev 6.2.0.0 (2015/02/27) SLABEL 対応
366         * @og.rev 6.2.0.0 (2015/02/27) キー:ラベル形式で表示するかどうかを、指定できるようにします。
367         *
368         * @param       buf                     タグ文字列のバッファー
369         * @param       value           選択されている値
370         * @param       useSelCache     Selection オブジェクトのキャッシュ機能を使用するかどうか。
371         * @param       useSlbl         ラベル(短)をベースとしたオプション表示を行うかどうか。
372         * @return      オプションタグ
373         */
374        private TagBuffer getOption( final TagBuffer buf,final String value,final boolean useSelCache,final boolean useSlbl ) {
375
376                final StringFormat format = new StringFormat( query, value, name );
377                final String newQuery = format.format();
378
379                // 6.2.0.0 (2015/02/27) キー:ラベル形式
380                final Selection selection = SelectionFactory.newDBSelection( newQuery, dbid, lang, addKeyLabel );
381
382                if( useSelCache ) {
383                        if( selection == bkSel ) { return null; }               // SelectionFactory で生成したキャッシュの確認なので、== で判定しておきます。
384                        bkSel = selection ;
385                }
386
387                // 6.1.1.0 (2015/01/17) TagBufferの連結記述
388                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
389                final String newValue = format.getValue();
390                return buf.addBody( selection.getOption( newValue, false, useSlbl ) );  // 6.2.0.0 (2015/02/27) SLABEL 対応
391        }
392}