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