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.db;
017
018import org.opengion.fukurou.system.HybsConst ;                                          // 6.1.0.0 (2014/12/26)
019import org.opengion.fukurou.util.ErrorMessage;
020import org.opengion.fukurou.db.Transaction;
021import org.opengion.hayabusa.common.HybsSystemException;
022import org.opengion.hayabusa.resource.ResourceManager;
023import org.opengion.hayabusa.html.ViewMarker;                                           // 8.0.0.0 (2021/09/30)
024
025import java.util.Locale;
026import java.util.Map;
027import java.util.LinkedHashMap ;
028import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
029import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
030import java.util.Collections;                                                                           // 6.4.3.1 (2016/02/12) refactoring
031
032/**
033 * AbstractTableFilter は、TableUpda インターフェースを継承した、DBTableModel 処理用の
034 * Abstract実装クラスです。
035 *
036 * @og.rev 5.5.2.6 (2012/05/25) protected変数をprivateに変更。インターフェースにメソッド追加
037 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。
038 *
039 * @version  0.9.0  2000/10/17
040 * @author   Kazuhiko Hasegawa
041 * @since    JDK1.1,
042 */
043public abstract class AbstractTableFilter implements TableFilter {
044        /** システムの改行コードを設定します。*/
045        protected static final String CR                 = HybsConst.CR;                        // 6.1.0.0 (2014/12/26) refactoring
046        /** StringBilderなどの初期値を設定します。   {@value} */
047        protected static final int BUFFER_MIDDLE = HybsConst.BUFFER_MIDDLE;     // 6.1.0.0 (2014/12/26) refactoring
048
049        // 5.5.2.6 (2012/05/25) protected変数をprivateに変更。インターフェースにメソッド追加
050        private DBTableModel    table           ;
051        private String                  modifyType      ;
052        private int[]                   rowNo           ;
053        private boolean                 useDebug        ;               // 6.0.2.5 (2014/10/31) refactoring メソッドと同じなので名称変更
054        private Transaction             tran            ;               // 5.1.9.0 (2010/08/01) 追加
055        private String                  sql                     ;               // 4.2.4.0 (2008/06/23)
056        private String                  dbid            ;               // 4.2.4.0 (2008/06/23)
057        private ResourceManager resource        ;               // 4.3.7.4 (2009/07/01)
058        private ViewMarker              viewMarker      ;               // 8.0.0.0 (2021/09/30)
059
060        private int                     errCode         = ErrorMessage.OK;
061        private ErrorMessage    errMessage      ;
062
063        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
064        private final ConcurrentMap<String,String> keyValMap = new ConcurrentHashMap<>();
065
066        /** 7.4.0.1 (2021/04/16) 値を返すための変数  */
067        private final ConcurrentMap<String,String> rtnValMap = new ConcurrentHashMap<>();
068
069        // 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
070        // 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更
071        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理を行います。  */
072        private final Map<String,String> keysMap = Collections.synchronizedMap( new LinkedHashMap<>() ) ;
073
074        // 6.0.2.3 (2014/10/10) plugin.table.TableFilter_XXXX から移動
075        /** XML宣言定数 */
076        protected static final String XML_START_TAG     = "<?xml version='1.0' encoding='UTF-8'?>" + CR + "<ROWSET tableName='xxx'>";
077        /** ROWSET 終了TAG {@value} */
078        protected static final String XML_END_TAG       = "</ROWSET>";
079        /** EXEC_SQL 開始TAG {@value} */
080        protected static final String EXEC_START_TAG= "<EXEC_SQL>";
081        /** EXEC_SQL exists="0" 開始TAG {@value} */
082        protected static final String EXEC_EXISTS_0_TAG= "<EXEC_SQL exists=\"0\">";             // 8.1.0.3 (2022/01/21)
083        /** EXEC_SQL 終了TAG {@value} */
084        protected static final String EXEC_END_TAG      = "</EXEC_SQL>";
085
086        // 6.0.2.3 (2014/10/10) isXml で、CR + EXEC_END_TAG のキャッシュを作成します。
087        /** XML形式かどうか  */
088        protected boolean       isXml           ;               // 6.0.2.3 (2014/10/10)
089        /** exists判定に使用するSQL文 */
090        protected String        execExistsSQL;          // 8.1.0.3 (2022/01/21)
091        /** 終了タグ */
092        protected String        execEndTag      ;               // 6.0.2.3 (2014/10/10)
093
094        /**
095         * デフォルトコンストラクター
096         *
097         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
098         */
099        protected AbstractTableFilter() { super(); }            // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
100
101        /**
102         * keys の整合性チェックを行うための初期設定を行います。
103         * サブクラスのコンストラクタ内で、設定するようにしてください。
104         *
105         * @og.rev 6.4.1.1 (2016/01/16) keys の整合性チェック対応
106         * @og.rev 6.4.3.1 (2016/02/12) ConcurrentMap 系は、key,val ともに not null 制限です。
107         *
108         * @param       key  整合性チェックを行うための keysMap に設定するキー
109         * @param       cmnt 整合性チェックを行うための キー の説明
110         */
111        protected void initSet( final String key , final String cmnt ) {
112                if( key != null && cmnt != null ) {
113                        keysMap.put( key , cmnt );
114                }
115        }
116
117        /**
118         * DBTableModel をセットします。
119         *
120         * @param       table DBTableModelオブジェクト
121         */
122        public void setDBTableModel( final DBTableModel table ) {
123                this.table = table;
124        }
125
126        /**
127         * DBTableModel を取得します。
128         *
129         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
130         *
131         * @return      内部のDBTableModel
132         */
133        public DBTableModel getDBTableModel() {
134                return table;
135        }
136
137        /**
138         * データ処理の方法(A:追加 C:更新 D:削除)を指定します。
139         *
140         * 通常は、DBTableModel に自動設定されている modifyType を元に、データ処理方法を
141         * 選別します。(A:追加 C:更新 D:削除)
142         * この場合、行単位で modifyType の値を取得して判別する必要がありますが、一般には
143         * 処理対象は、全件おなじ modifyType である可能性が高いです。
144         * また、selectedAll などで強制的に全件処理対象とする場合は、modifyType に値が
145         * 設定さていません。その様な場合に外部より modifyType を指定します。
146         * 初期値は、自動判定 です。
147         *
148         * @og.rev 5.5.2.6 (2012/05/25) 廃止
149         *
150         * @param  type データ処理の方法(A:追加 C:更新 D:削除)
151         */
152        public void setModifyType( final String type ) {
153                modifyType = type;
154        }
155
156        /**
157         * データ処理の方法(A:追加 C:更新 D:削除)を取得します。
158         *
159         * 初期値は、自動判定 です。
160         *
161         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
162         *
163         * @return  データ処理の方法(A:追加 C:更新 D:削除)
164         */
165        public String getModifyType() {
166                return modifyType ;
167        }
168
169        /**
170         * キーと値のペアの変数配列を受け取ります。
171         *
172         * ここでは、この方式以外に、パラメーターMapを受け取る方法もあります。
173         * この受け取る時に、キーを大文字化します。TableFilter の keys は、
174         * 大文字のみで定義しておくことで、HTMLやWindows世代の曖昧な表記方法に
175         * 対応しています。(unixやxmlのような厳格な方が好きですけど)
176         *
177         * keys,vals とパラメーターMapを同時に指定した場合は、両方とも有効です。
178         * ただし、キーが重複した場合は、不定と考えてください。
179         *
180         * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
181         *
182         * @param   keys キー配列
183         * @param   vals 値配列
184         * @see         #setParamMap( ConcurrentMap )
185         */
186        public void setKeysVals( final String[] keys,final String[] vals ) {
187                if( keys != null && vals != null ) {
188                        for( int i=0; i<keys.length; i++ ) {
189                                // 5.6.6.0 (2013/07/05) 共通のセッターメソッド経由で登録します。
190                                setKeyVal( keys[i],vals[i] );
191                        }
192                }
193        }
194
195        /**
196         * キーと値のペアを受け取り、内部の keyValMap マップに追加します。
197         *
198         * キーか値のどちらかが null の場合は、何もしません。つまり、val に
199         * null をセットすることはできません。
200         *
201         * このメソッドは、setKeysVals( String[] ,String[] ) メソッドと、
202         * setParamMap( Map<String,String> ) メソッドの両方から、使用します。
203         * 処理を行うに当たり、下記の処理を行います。
204         * 1.キーを大文字化します。
205         * 2.各クラスの keys と整合性チェックを行います。
206         *
207         * ただし、setKeysVals と setParamMap の登録順は、不定と考えてください。
208         * 両方に同じキーを指定すると、どちらの値がセットされたかは、不定です。
209         *
210         * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
211         * @og.rev 6.4.3.4 (2016/03/12) Map#forEach で対応する。
212         * @og.rev 6.7.9.1 (2017/05/19) keysMap が、空の場合も、keyValMap に登録する。(initSet 未登録時)
213         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
214         * @og.rev 8.0.0.0 (2021/09/30) brタグを止めてを、preタグに変更します。
215         *
216         * @param   key キー文字列(null の場合は、処理しない)
217         * @param   val 値文字列(null の場合は、処理しない)
218         * @see         #setKeysVals( String[] ,String[] )
219         * @see         #setParamMap( ConcurrentMap )
220         */
221        private void setKeyVal( final String key,final String val ) {
222                // key か val かどちらかが null の場合は、処理を行わない。
223                if( key == null || val == null ) { return; }
224
225                final String upKey = key.toUpperCase(Locale.JAPAN);
226
227                // 6.7.9.1 (2017/05/19) keysMap が、空の場合も、keyValMap に登録する。(initSet 未登録時)
228                if(  keysMap.isEmpty() || keysMap.containsKey( upKey ) ) {              // keysMap は、各サブクラスで定義
229                        keyValMap.put( upKey,val );
230                }
231                else {
232//                      final String BR = "<br />" + CR ;
233//                      final String BR = "<br>" + CR ;                         // 7.0.1.0 (2018/10/15)
234                        final String BR = CR ;                                          // 8.0.0.0 (2021/09/30)
235                        final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE );
236                        // 6.0.2.5 (2014/10/31) char を append する。
237//                      errMsg.append( BR )
238                        errMsg.append( "<pre>" ).append( BR )
239                                  .append( "指定のキーは、この tableFilter では、使用できません。" ).append( BR )
240                                  .append( "  class=[" ).append( getClass().getName() ).append( ']' ).append( BR )
241                                  .append( "  key  =[" ).append( key                              ).append( ']' ).append( BR )
242                                  .append( "  ======== usage keys ======== " ).append( BR ) ;
243                        // 6.4.3.4 (2016/03/12) Map#forEach で対応する。
244                        keysMap.forEach( (k,v) -> { errMsg.append( ' ' ).append( k ).append( ':' ).append( v ).append( BR ); } );
245                        errMsg.append( "  ============================ " ).append( BR ).append( "</pre>" );
246
247                        throw new HybsSystemException( errMsg.toString() );
248                }
249        }
250
251        /**
252         * 選択された行番号の配列をセットします。
253         *
254         * 表示データの HybsSystem.ROW_SELECTED_KEY を元に、選ばれた 行を
255         * 処理の対象とします。
256         *
257         * @param   rowNoTmp 行番号配列(可変長引数)
258         */
259        public void setParameterRows( final int... rowNoTmp ) {
260                if( rowNoTmp != null && rowNoTmp.length > 0 ) {         // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
261                        final int size = rowNoTmp.length ;
262                        rowNo = new int[size];
263                        System.arraycopy( rowNoTmp,0,rowNo,0,size );
264                }
265        }
266
267        /**
268         * 選択された行番号の配列を取得します。
269         *
270         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
271         * 処理の対象とします。
272         *
273         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
274         * @og.rev 6.0.2.5 (2014/10/31) null ではなく、サイズ0の配列を返すように変更。
275         *
276         * @return   行番号の配列(選ばれていない場合は、サイズ0の配列を返す)
277         * @og.rtnNotNull
278         */
279        public int[] getParameterRows() {
280                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
281                return rowNo == null ? new int[0] : rowNo.clone() ;
282        }
283
284        /**
285         * アクセスログ取得の為,Transactionオブジェクトを設定します。
286         *
287         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応(新規追加)
288         *
289         * @param   tran Transactionオブジェクト
290         */
291        public void setTransaction( final Transaction tran ) {
292                this.tran = tran;
293        }
294
295        /**
296         * アクセスログ取得の為,Transactionオブジェクトを取得します。
297         *
298         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応(新規追加)
299         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
300         *
301         * @return   Transactionオブジェクト
302         */
303        public Transaction getTransaction() {
304                return tran;
305        }
306
307        /**
308         * DBIDを指定します。
309         *
310         * @og.rev 4.2.4.0 (2008/06/23) 新規追加
311         *
312         * @param dbid 接続先ID
313         */
314        public void setDbid( final String dbid ) {
315                this.dbid = dbid;
316        }
317
318        /**
319         * DBIDを取得します。
320         *
321         * @og.rev 4.2.4.0 (2008/06/23) 新規追加
322         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
323         *
324         * @return DBID(接続先情報)
325         */
326        public String getDbid() {
327                return dbid;
328        }
329
330        /**
331         * ボディー部分のSQLを指定します。
332         *
333         * @og.rev 4.2.4.0 (2008/06/23) 新規追加
334         *
335         * @param sql ボディー部分のSQL
336         */
337        public void setSql( final String sql ) {
338                this.sql = sql;
339        }
340
341        /**
342         * ボディー部分のSQLを取得します。
343         *
344         * @og.rev 4.2.4.0 (2008/06/23) 新規追加
345         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
346         *
347         * @return ボディー部分のSQL
348         */
349        public String getSql() {
350                return sql;
351        }
352
353        /**
354         * パラメーターMapを指定します。
355         *
356         * keys,vals と パラメーターMapを同時に指定した場合は、両方とも有効です。
357         * ただし、キーが重複した場合は、不定と考えてください。
358         *
359         * この受け取る時に、キーを大文字化します。TableFilter の keys は、
360         * 大文字のみで定義しておくことで、HTMLやWindows世代の曖昧な表記方法に
361         * 対応しています。(unixやxmlのような厳格な方が好きですけど)
362         *
363         * @og.rev 5.6.5.2 (2013/06/21) 新規追加
364         * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
365         * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap を受け取ることを明確にするため、I/FをConcurrentMapに変更します。
366         *
367         * @param paramMap パラメーターMap
368         * @see         #setKeysVals( String[] ,String[] )
369         */
370        public void setParamMap( final ConcurrentMap<String,String> paramMap ) {
371                // 6.4.3.3 (2016/03/04) Map#forEach に変更
372                if( paramMap != null ) {
373                        paramMap.forEach( (k,v) -> setKeyVal( k,v ) );
374                }
375        }
376
377        /**
378         * リソースオブジェクトを指定します。
379         *
380         * @og.rev 4.3.7.4 (2009/07/01) 新規追加
381         *
382         * @param resource リソースオブジェクト
383         */
384        public void setResource( final ResourceManager resource ) {
385                this.resource = resource;
386        }
387
388        /**
389         * リソースオブジェクトを取得します。
390         *
391         * @og.rev 4.3.7.4 (2009/07/01) 新規追加
392         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
393         *
394         * @return リソースオブジェクト
395         */
396        public ResourceManager getResource() {
397                return resource;
398        }
399
400        /**
401         * viewMarkerオブジェクトを指定します。
402         *
403         * @og.rev 8.0.0.0 (2021/09/30) viewMarker追加
404         *
405         * @param marker viewMarkerオブジェクト
406         */
407        public void setViewMarker( final ViewMarker marker ) {
408                viewMarker = marker;
409        }
410
411        /**
412         * viewMarkerオブジェクトを取得します。
413         *
414         * @og.rev 8.0.0.0 (2021/09/30) viewMarker追加
415         *
416         * @return viewMarkerオブジェクト
417         */
418        public ViewMarker getViewMarker() {
419                return viewMarker;
420        }
421
422        /**
423         * 値を返すためのMapを返します。
424         *
425         * Mapそのものを返しますので、中身の書き換えは行わないでください。
426         *
427         * @og.rev 7.4.0.1 (2021/04/16) 値を返すための変数
428         *
429         * @return Mapオブジェクト
430         */
431        public Map<String,String> getReturnMap() {
432                return rtnValMap;
433        }
434
435        /**
436         * デバッグ情報を出力するかどうか[true:する/false:しない]を指定します。
437         * true でデバッグ情報を表示します。
438         *
439         * @param   flag  デバッグ出力するか [true:する/false:しない]
440         */
441        public void setDebug( final boolean flag ) {
442                useDebug = flag;        // 6.0.2.5 (2014/10/31) refactoring メソッドと同じなので名称変更
443        }
444
445        /**
446         * デバッグ情報を出力するかどうか[true:する/false:しない]を取得します。
447         * true でデバッグ情報を表示します。
448         *
449         * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
450         *
451         * @return  デバッグ出力 [true:する/false:しない]
452         */
453        public boolean isDebug() {
454                return useDebug ;       // 6.0.2.5 (2014/10/31) refactoring メソッドと同じなので名称変更
455        }
456
457        /**
458         * エラーコード を取得します。
459         * エラーコード は、ErrorMessage クラスで規定されているコードです。
460         *
461         * @return   エラーコード
462         */
463        public int getErrorCode() {
464                return errCode;
465        }
466
467        /**
468         * エラーメッセージオブジェクト を取得します。
469         *
470         * @return   エラーメッセージオブジェクト
471         */
472        public ErrorMessage getErrorMessage() {
473                return errMessage;
474        }
475
476        /**
477         * タイトルとエラーコードを指定して、エラーメッセージオブジェクト を作成します。
478         * すでに、作成済みの場合は、作成済みのオブジェクトを、まだ、未作成の場合は、
479         * 新規に作成します。
480         *
481         * @param       title   タイトル
482         * @param       code    エラーコード
483         *
484         * @return      エラーメッセージオブジェクト
485         */
486        protected ErrorMessage makeErrorMessage( final String title,final int code ) {
487                if( errMessage == null ) {
488                        errMessage = new ErrorMessage( title );
489                }
490                if( errCode < code ) { errCode = code; }
491                return errMessage;
492        }
493
494        /**
495         *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
496         *
497         * @param       nameArray カラム名配列
498         *
499         * @return      カラムNo配列(可変長引数)
500         */
501        protected int[] getTableColumnNo( final String... nameArray ) {
502                int[] clmNo = new int[nameArray.length];
503                for( int i=0; i<clmNo.length; i++ ) {
504                        clmNo[i] = table.getColumnNo( nameArray[i] );
505                }
506                return clmNo;
507        }
508
509        /**
510         * 設定されたパラメータキーに対する値を取得します。
511         * 引数、および、パラメータが null の場合は、 null を返します。
512         *
513         * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加
514         *
515         * @param       key     パラメータキー
516         *
517         * @return      パラメータ値
518         */
519        protected String getValue( final String key ) {
520                return key == null ? null : keyValMap.get( key );
521        }
522
523        /**
524         * フィルターからtaglibのリクエスト変数に値を書き戻したい場合に、key と val をセットします。
525         * 引数、および、パラメータが null の場合は、何もしません。
526         *
527         * @og.rev 7.4.0.1 (2021/04/16) 値を返すための変数セット
528         *
529         * @param       key     戻しキー
530         * @param       val     戻し値
531         */
532        protected void setValue( final String key ,final String val ) {
533                if( key != null && val != null ) {
534                        rtnValMap.put( key,val );
535                }
536        }
537
538        /**
539         * keyValMapに持っているキーの配列を取得します。
540         * これは、サブクラスで、initSet(String,String) を行わない場合、keys には
541         * 値を自由に設定できます。
542         * その値を取り出すためです。
543         *
544         * @og.rev 6.7.9.1 (2017/05/19) 新規追加
545         *
546         * @return      キー値の配列(keyValMapに持っているキー)
547         */
548        protected String[] getKeys() {
549                return keyValMap.keySet().toArray( new String[keyValMap.size()] );
550        }
551}