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