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.fukurou.business;
017
018import java.sql.Connection;
019import java.sql.ParameterMetaData;
020import java.sql.PreparedStatement;
021import java.sql.ResultSet;
022import java.sql.ResultSetMetaData;
023import java.sql.SQLException;
024import java.util.Map;
025import java.util.HashMap;                                                                                       // 6.4.3.3 (2016/03/04) not null調査が済むまで、元に戻します。
026import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
027import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
028import java.util.Locale;
029import java.util.Set;
030import java.util.Arrays;
031
032import org.opengion.fukurou.system.OgRuntimeException ;                         // 6.4.2.0 (2016/01/29)
033import org.opengion.fukurou.db.ConnectionFactory;
034import org.opengion.fukurou.db.DBFunctionName;
035import org.opengion.fukurou.db.DBUtil;
036import org.opengion.fukurou.db.Transaction;
037import org.opengion.fukurou.model.Formatter;
038import org.opengion.fukurou.model.DataModel;                                            // 6.7.9.1 (2017/05/19)
039import org.opengion.fukurou.system.DateSet;                                                     // 6.4.2.0 (2016/01/29)
040import org.opengion.fukurou.util.ErrMsg;
041import org.opengion.fukurou.util.ErrorMessage;
042import org.opengion.fukurou.util.HybsLoader;
043import org.opengion.fukurou.util.StringUtil;
044import org.opengion.fukurou.util.SystemParameter;
045import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
046import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
047import static org.opengion.fukurou.system.HybsConst.DB_FETCH_SIZE;      // 6.9.4.1 (2018/04/09)
048
049/**
050 * 業務ロジックを処理するために必要な共通メソッドの実行を行っている抽象クラスです。
051 *
052 * メインロジックについては、各サブクラスで実装する必要があります。
053 *
054 * @og.rev 5.1.1.0 (2009/12/01) 新規作成
055 * @og.group 業務ロジック
056 *
057 * @version 5.0
058 * @author Hiroki Nakamura
059 * @since JDK1.6,
060 */
061public abstract class AbstractBizLogic {
062
063        /** エラーメッセージをセットする際に使用します {@value} */
064        protected static final int OK        = ErrorMessage.OK;
065        /** エラーメッセージをセットする際に使用します {@value} */
066        protected static final int WARNING   = ErrorMessage.WARNING;
067        /** エラーメッセージをセットする際に使用します {@value} */
068        protected static final int NG        = ErrorMessage.NG;
069        /** エラーメッセージをセットする際に使用します {@value} */
070        protected static final int EXCEPTION = ErrorMessage.EXCEPTION;
071        /** エラーメッセージをセットする際に使用します {@value} */
072        protected static final int ORCL_ERR  = ErrorMessage.ORCL_ERR;
073
074//      /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ  {@value} */
075//      private static final int DB_FETCH_SIZE = 1001 ;
076
077        private Connection      conn    ;
078        private Transaction tran        ;                       // 5.1.9.0 (2010/08/01) シーケンス対応
079        private String          dbid    ;                       // 5.1.9.0 (2010/08/01) シーケンス対応
080        /** データベースファンクション */
081        protected DBFunctionName        dbName  ;       // 5.1.9.0 (2010/08/01) シーケンス対応
082        private HybsLoader      loader  ;
083        private String[]        keys    ;
084        private String[]        vals    ;
085        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
086        private final Map<String, String> variableMap  = new HashMap<>();                                                               // 6.4.3.3 (2016/03/04) not null調査が済むまで、元に戻します。
087        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
088        private final ConcurrentMap<String, Formatter> formatMap = new ConcurrentHashMap<>();
089        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
090        private final ConcurrentMap<String, SystemParameter> sysParamMap = new ConcurrentHashMap<>();
091        private final ErrorMessage errMsg = new ErrorMessage();
092        private String bizRtn           ;                                       // 5.1.8.0 (2010/07/01) メソッド名と変数名を分ける。
093        private boolean debugFlag       ;                                       // 5.1.8.0 (2010/07/01) メソッド名と変数名を分ける。
094
095        private final StringBuilder debugMsg = new StringBuilder( BUFFER_MIDDLE );
096        private boolean useParamMetaData        ;                       // 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)
097
098        private final ConcurrentMap<String, String> rtnMap  = new ConcurrentHashMap<>();                // 6.9.9.0 (2018/08/20) 戻り値を返せるようにします。
099
100        /**
101         * 配列側テーブルモデル
102         *
103         * 配列型テーブルモデル自体は、protected属性であるため、サブクラスから直接参照することができます。
104         * 但し、これは、各業務ロジックで直接参照することを想定したものではなく、BizLogicの
105         * メイン構造を拡張するサブクラスを定義する際に使用することを想定しています。
106         * (この想定がなければ、本来は、package privateにすべきです)
107         * このため、業務ロジックを各実装クラスでは直接参照しないで下さい。
108         *
109         * @og.rev 6.7.9.1 (2017/05/19) protected ArrayTableModel を、private DataModel に変更します。
110         */
111        private DataModel<String> table ;                               // 6.7.9.1 (2017/05/19)
112
113        /**
114         * 配列型テーブルモデルの現在の処理行
115         *
116         * 行番号自体は、protected属性であるため、サブクラスから直接参照することができます。
117         * 但し、これは、各業務ロジックで直接参照することを想定したものではなく、BizLogicの
118         * メイン構造を拡張するサブクラスを定義する際に使用することを想定しています。
119         * (この想定がなければ、本来は、package privateにすべきです)
120         * このため、業務ロジックを各実装クラスでは直接参照しないで下さい。
121         *
122         * ※ インデックス(row)とは、このArrayTableModel に持つ vals 配列の行のインデックスです。
123         * よって、オリジナルのDBTableModelの行番号ではありません。
124         */
125        protected int row = -1;
126
127        /**
128         * デフォルトコンストラクター
129         *
130         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
131         */
132        protected AbstractBizLogic() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
133
134        /**
135         * DBのトランザクションオブジェクトを指定します。
136         * 各実装クラスでは、コネクションのcommit,rollbackは行われません。
137         * (全てのDB処理は、1つのトランザクションとして処理されます。)
138         * このため、commit,rollbackは呼び出し元で行う必要があります。
139         * このメソッドは、1度しかセットすることができません。2回以上呼び出しするとエラーになります。
140         *
141         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
142         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)
143         *
144         * @param tr トランザクション
145         */
146        public void setTransaction( final Transaction tr ) {
147                tran = tr;
148                conn = tran.getConnection( dbid );
149                useParamMetaData = ConnectionFactory.useParameterMetaData( dbid );      // 5.3.8.0 (2011/08/01)
150        }
151
152        /**
153         * DBのトランザクションオブジェクトを返します。
154         *
155         * (全てのDB処理は、1つのトランザクションとして処理されます。)
156         *
157         * @og.rev 7.4.2.0 (2021/05/14) 外部から指定するTransactionオブジェクト 対応
158         *
159         * @return トランザクション
160         */
161        public Transaction getTransaction() {
162                return tran;
163        }
164
165        /**
166         * 接続先IDを指定します。
167         * このメソッドは、1度しかセットすることができません。2回以上呼び出しするとエラーになります。
168         *
169         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
170         *
171         * @param id 接続先ID
172         */
173        /* default */ void setDbid( final String id ) {
174                dbid = id;
175        }
176
177        /**
178         * 業務ロジックのクラスをロードするためのクラスローダーをセットします。
179         * このメソッドは、1度しかセットすることができません。2回以上呼び出しするとエラーになります。
180         *
181         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
182         *
183         * @param ldr クラスローダー
184         */
185        /* default */ void setLoader( final HybsLoader ldr ) {
186                if( loader != null ) {
187                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
188                        final String errMsg = "既にクラスローダーがセットされています。"
189                                                                        + " OLD:" + loader
190                                                                        + " IN :" + ldr ;
191                        throw new OgRuntimeException( errMsg );
192                }
193                loader = ldr;
194        }
195
196        /**
197         * 配列型テーブルモデルをセットします。
198         * このメソッドは、1度しかセットすることができません。2回以上呼び出しするとエラーになります。
199         *
200         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
201         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
202         *
203         * @param tbl 配列型テーブルモデル
204         */
205        /* default */ void setTable( final DataModel<String> tbl ) {
206                if( table != null ) {
207                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
208                        final String errMsg = "既に配列型テーブルモデルがセットされています。"
209                                                                + " OLD:" + table
210                                                                + " IN :" + tbl ;
211                        throw new OgRuntimeException( errMsg );
212                }
213                table = tbl;
214        }
215
216        /**
217         * 配列型テーブルモデルを取得します。
218         *
219         * @og.rev 6.7.9.1 (2017/05/19) 新規追加
220         *
221         * @return 配列型テーブルモデル
222         */
223        protected DataModel<String> getTable() {
224                return table ;
225        }
226
227        /**
228         * 固定値のキー配列を指定します。
229         * このメソッドは、1度しかセットすることができません。2回以上呼び出しするとエラーになります。
230         *
231         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
232         *
233         * @param ks キー配列(可変長引数)
234         */
235        /* default */ void setKeys( final String... ks ) {
236                if( keys != null ) {
237                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
238                        final String errMsg = "既に固定値配列(キー)がセットされています。"  + CR
239                                                        + "   KESY   =" + Arrays.toString( keys )                       + CR
240                                                        + "   in keys=" + Arrays.toString( ks ) ;
241                        throw new OgRuntimeException( errMsg );
242                }
243                if( ks != null && ks.length > 0 ) { keys = ks; }                // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
244        }
245
246        /**
247         * 固定値の値配列を指定します。
248         * このメソッドは、1度しかセットすることができません。2回以上呼び出しするとエラーになります。
249         *
250         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
251         *
252         * @param vs 値配列(可変長引数)
253         */
254        /* default */ void setVals( final String... vs ) {
255                if( vals != null ) {
256                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
257                        final String errMsg = "既に固定値配列(値)がセットされています。"   + CR
258                                                        + "   VALS   =" + Arrays.toString( vals )               + CR
259                                                        + "   in vals=" + Arrays.toString( vs ) ;
260                        throw new OgRuntimeException( errMsg );
261                }
262                if( vs != null && vs.length > 0 ) { vals = vs; }                // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
263        }
264
265        /**
266         * この処理の実行ユーザーIDを指定します。
267         *
268         * @param id 実行ユーザーID(not null)
269         */
270        /* default */ void setUserId( final String id ) {
271                variableMap.put( "CON.USERID", id);
272        }
273
274        /**
275         * 親(呼び出し)PGIDを指定します。
276         *
277         * @param id 親PGID
278         */
279        /* default */ void setParentPgId( final String id ) {
280                variableMap.put( "CON.PGPID", id );
281        }
282
283        /**
284         * デバッグモードにします。
285         */
286        /* default */ void setDebug() {
287                debugFlag = true;
288        }
289
290        /**
291         * デバッグメッセージを取得します。
292         *
293         * @return デバッグメッセージ
294         * @og.rtnNotNull
295         */
296        /* default */ String getDebugMsg() {
297                return debugMsg.toString();
298        }
299
300        /**
301         * 処理を実行します。
302         * 処理の方法は、main()メソッドにより定義されます。
303         * 実装クラスで発生した全ての例外は、Throwableオブジェクトとしてスローされます。
304         * 呼び出し元では、例外を確実にcatchして、commit,rollbackを行ってください。
305         *
306         * @og.rev 5.1.9.0 (2010/08/01) シーケンス対応
307         *
308         * @return 処理が成功したかどうか
309         * @throws Throwable 実行時の全エラーを上位に転送します。
310         */
311        /* default */ boolean exec() throws Throwable {
312                dbName = DBFunctionName.getDBName( ConnectionFactory.getDBName( dbid ) );
313                makeParamMap();
314                init();
315
316                return main();
317        }
318
319        /**
320         * 処理のメインロジックの前処理を記述します。
321         *
322         * このメソッド自体は、protected属性であるため、サブクラスから直接参照することができます。
323         * 但し、これは、各業務ロジックで直接参照することを想定したものではなく、BizLogicの
324         * メイン構造を拡張するサブクラスを定義する際に使用することを想定しています。
325         * (この想定がなければ、本来は、package privateにすべきです)
326         * このため、業務ロジックを各実装クラスでは直接参照しないで下さい。
327         */
328        protected abstract void init();
329
330        /**
331         * 処理のメインロジックを記述します。
332         *
333         * このメソッド自体は、protected属性であるため、サブクラスから直接参照することができます。
334         * 但し、これは、各業務ロジックで直接参照することを想定したものではなく、BizLogicの
335         * メイン構造を拡張するサブクラスを定義する際に使用することを想定しています。
336         * (この想定がなければ、本来は、package privateにすべきです)
337         * このため、業務ロジックを各実装クラスでは直接参照しないで下さい。
338         *
339         * @return 処理が正常終了したか
340         */
341        protected abstract boolean main();
342
343        /**
344         * 結果ステータスを返します。
345         *
346         * @return 結果ステータス
347         */
348        /* default */ int getKekka() {
349                return errMsg.getKekka();
350        }
351
352        /**
353         * エラーメッセージオブジェクトを返します。
354         *
355         * @return エラーメッセージ
356         */
357        /* default */ ErrorMessage getErrMsg() {
358                return errMsg;
359        }
360
361        /**
362         * 業務ロジックの戻り値を返します。
363         *
364         * @return 戻り値
365         */
366        /* default */ String getReturn() {
367                return bizRtn;
368        }
369
370        /**
371         * 業務ロジックを実行するために、テーブルモデルが外部からセットされる必要があるか
372         * を返します。
373         * 必須である場合、その業務ロジックは、子ロジックとして呼び出すことができません。
374         * これは、子ロジック呼び出し時は、テーブルモデルがセットされないためです。
375         * (このクラスは、テーブルモデルが外部から指定されている必要はありません。)
376         *
377         * このメソッド自体は、protected属性であるため、サブクラスから直接参照することができます。
378         * 但し、これは、各業務ロジックで直接参照することを想定したものではなく、BizLogicの
379         * メイン構造を拡張するサブクラスを定義する際に使用することを想定しています。
380         * (この想定がなければ、本来は、package privateにすべきです)
381         * このため、業務ロジックを各実装クラスでは直接参照しないで下さい。
382         *
383         * @return      テーブルモデルが外部からセットされる必要があるかどうか(常にfalse)
384         */
385        protected boolean isRequireTable() {
386                return false;
387        }
388
389        /**
390         * デバッグモードかどうかを返します。
391         *
392         * @return デバッグモードかどうか
393         */
394        protected final boolean isDebug() {
395                return debugFlag;
396        }
397
398        /**
399         * デバッグメッセージを追加します。
400         *
401         * @param msg 追加するデバッグメッセージ
402         */
403        protected final void debug( final String msg ) {
404                debugMsg.append( msg ).append( CR );
405        }
406
407        /**
408         * 指定されたキーの値を返します。
409         *
410         * @param key キー
411         *
412         * @return 変数値
413         */
414        protected final String var( final String key ) {
415                return variableMap.get( key );
416        }
417
418        /**
419         * 指定されたキーの値をint型に変換して返します。
420         *
421         * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も、0 を返します。
422         *
423         * @param key キー
424         *
425         * @return 変数値
426         */
427        protected final int vari( final String key ) {
428                return str2int( var( key ) );                   // 6.7.9.1 (2017/05/19)
429        }
430
431        /**
432         * 指定されたキーの値をdouble型に変換して返します。
433         *
434         * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も、0 を返します。
435         *
436         * @param key キー
437         *
438         * @return 変数値
439         */
440        protected final double vard( final String key ) {
441                return str2dbl( var( key ) );                   // 6.7.9.1 (2017/05/19)
442        }
443
444        /**
445         * パラメーターのキー一覧を配列形式で返します。
446         * このパラメーターは、業務ロジック内でセットされたパラメーターも含まれますのでご注意下さい。
447         *
448         * @return パラメーターのキー配列
449         */
450        protected final String[] varKeys() {
451                final Set<String> keys = variableMap.keySet();
452                return keys.toArray( new String[keys.size()] );
453        }
454
455        /**
456         * 指定されたキーで値を登録します。
457         * パラメーターとしてこの業務ロジックが呼ばれる際の引数となっている場合は、
458         * エラーとなります。
459         *
460         * @og.rev 5.2.1.0 (2010/10/01) チェックのバグを修正
461         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
462         *
463         * @param key キー
464         * @param val 値
465         */
466        protected final void set( final String key, final String val ) {
467                // 6.0.2.5 (2014/10/31) 素直に、variableMap で、キー有無を判定する。
468                if( variableMap.containsKey( key ) ) {
469                        final String errMsg = "すでに登録済みのキーを定義することはできません。"        + CR
470                                                        + "   key =" + key                              + CR
471                                                        + "   val =" + val                              + CR
472                                                        + "   元  =" + variableMap.get( key ) ;
473                        throw new OgRuntimeException( errMsg );
474                }
475                variableMap.put( key, val );
476        }
477
478        /**
479         * 指定されたキーで値を登録します。
480         * パラメーターとしてこの業務ロジックが呼ばれる際の引数となっている場合は、
481         * エラーとなります。
482         *
483         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
484         *
485         * @param key キー
486         * @param val 値
487         */
488        protected final void set( final String key, final int val ) {
489                set( key, String.valueOf( val ) );
490        }
491
492        /**
493         * 指定されたキーで値(double型)を登録します。
494         * パラメーターとしてこの業務ロジックが呼ばれる際の引数となっている場合は、
495         * エラーとなります。
496         *
497         * @og.rev 5.1.9.0 (2010/08/01) 新規作成
498         *
499         * @param key キー
500         * @param val 値
501         */
502        protected final void set( final String key, final double val ) {
503                set( key, String.valueOf( val ) );
504        }
505
506        /**
507         * 処理中の行の指定されたキー(カラム名)の値を返します。
508         *
509         * @param key キー
510         *
511         * @return 値
512         */
513        protected final String line( final String key ) {
514                return line( key, row );
515        }
516
517        /**
518         * メインの配列型テーブルモデルに対して、行を指定して値を取得します。
519         * 指定された行が範囲を超えている場合は、nullを返します。
520         *
521         * @og.rev 5.1.8.0 (2010/07/01) テーブルに存在しないカラム名を指定した場合に、NullPointerExceptionが発生するバグを修正
522         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
523         *
524         * @param key キー
525         * @param rw 行番号(インデックス)
526         *
527         * @return 値
528         */
529        protected final String line( final String key, final int rw ) {
530                if( table == null ) {
531                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
532                        final String errMsg = "配列型テーブルモデルがセットされていないため、#line( String,int )メソッドはできません。"   + CR
533                                                        + "   line( " + key + "," + rw + " );"  + CR ;
534                        throw new OgRuntimeException( errMsg );
535                }
536                // 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
537
538                final int col = table.getColumnNo( key );
539
540                return col < 0 || rw < 0 || rw >= table.getRowCount() ? null : table.getValue( rw, col );
541        }
542
543        /**
544         * 処理中の行の指定されたカラム番号の値を返します。
545         * line( String )は、毎回、カラム番号を取得しているため、非効率です。
546         * ただし、一旦カラム名から、カラム番号を取得し、それを使用するのと、
547         * linei(String)や、lined(String) などの直接的なメソッドもないため、
548         * 利用者側でそのあたりの処理を入れる必要があります。
549         *
550         * @og.rev 6.7.9.1 (2017/05/19) 文字列を整数に変換します。
551         *
552         * @param col カラム番号
553         * @return 値
554         */
555        protected final String line( final int col ) {
556                return line( col, row );
557        }
558
559        /**
560         * メインの配列型テーブルモデルに対して、行を指定して値を取得します。
561         * 指定された行が範囲を超えている場合は、nullを返します。
562         *
563         * @og.rev 6.7.9.1 (2017/05/19) 文字列を整数に変換します。
564         *
565         * @param col カラム番号
566         * @param rw 行番号(インデックス)
567         * @return 値
568         */
569        protected final String line( final int col, final int rw ) {
570                if( table == null ) {
571                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
572                        final String errMsg = "配列型テーブルモデルがセットされていないため、#line( String,int )メソッドはできません。"   + CR
573                                                        + "   line( " + col + "," + rw + " );"  + CR ;
574                        throw new OgRuntimeException( errMsg );
575                }
576
577                return col < 0 || rw < 0 || rw >= table.getRowCount() ? null : table.getValue( rw, col );
578        }
579
580        /**
581         * 処理中の行の指定されたキー(カラム名)の値をint型に変換して返します。
582         *
583         * @og.rev 6.7.9.0 (2017/04/28) row を使用して、#linei( String,int )を呼びます。
584         *
585         * @param key キー
586         *
587         * @return 値
588         */
589        protected final int linei( final String key ) {
590                return str2int( line( key, row ) );                     // 6.7.9.1 (2017/05/19)
591        }
592
593        /**
594         * メインの配列型テーブルモデルに対して、行を指定して値をint型に変換して返します。
595         * 指定された行が範囲を超えている場合は、nullを返します。
596         *
597         * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も、0 を返します。
598         *
599         * @param key キー
600         * @param rw 行番号(インデックス)
601         *
602         * @return 値
603         */
604        protected final int linei( final String key, final int rw ) {
605                return str2int( line( key, rw ) );                      // 6.7.9.1 (2017/05/19)
606        }
607
608        /**
609         * 処理中の行の指定されたキー(カラム名)の値をdouble型に変換して返します。
610         *
611         * @og.rev 6.7.9.0 (2017/04/28) row を使用して、#lined( String,int )を呼びます。
612         *
613         * @param key キー
614         *
615         * @return 値
616         */
617        protected final double lined( final String key ) {
618                return str2dbl( line( key, row ) );                     // 6.7.9.1 (2017/05/19)
619        }
620
621        /**
622         * メインの配列型テーブルモデルに対して、行を指定して値をdouble型に変換して返します。
623         * 指定された行が範囲を超えている場合は、nullを返します。
624         *
625         * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も、0 を返します。
626         *
627         * @param key キー
628         * @param rw 行番号(インデックス)
629         *
630         * @return 値
631         */
632        protected final double lined( final String key, final int rw ) {
633                return str2dbl( line( key, rw ) );                      // 6.7.9.1 (2017/05/19)
634        }
635
636        /**
637         * 指定のカラム名引数に相当するデータを2重配列で返します。
638         *
639         * @og.rev 6.8.5.0 (2018/01/09) 新規追加
640         *
641         * @param       clmNms  値が参照されるカラム名配列(可変長引数)
642         *
643         * @return  指定された名引数に相当するデータの2重配列
644         * @og.rtnNotNull
645         */
646        protected String[][] getValues( final String... clmNms ) {
647                // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
648                if( table == null ) {
649                        final String errMsg = "配列型テーブルモデルがセットされていないため、#getValues( String... )メソッドはできません。"       + CR
650                                                        + "   clmNms= " + Arrays.toString( clmNms ) + " );"     + CR ;
651                        throw new OgRuntimeException( errMsg );
652                }
653
654                return ((ArrayTableModel)table).getValues( clmNms );
655        }
656
657        /**
658         * 文字列を整数に変換します。
659         * 文字列が、nullか、空文字列の場合は、0 を返します。
660         *
661         * @og.rev 6.7.9.1 (2017/05/19) 文字列を整数に変換します。
662         *
663         * @param val 入力文字列
664         * @return int値
665         */
666        protected final int str2int( final String val ) {
667                return val == null || val.isEmpty() ? 0 : Integer.parseInt( val );
668        }
669
670        /**
671         * 文字列をdoubleに変換します。
672         * 文字列が、nullか、空文字列の場合は、0d を返します。
673         *
674         * @og.rev 6.7.9.1 (2017/05/19) 文字列をdoubleに変換します。
675         *
676         * @param val 入力文字列
677         * @return double値
678         */
679        protected final double str2dbl( final String val ) {
680                return val == null || val.isEmpty() ? 0d : Double.parseDouble( val );
681        }
682
683        /**
684         * 文字列配列をdouble配列に変換します。
685         * 文字列が、nullか、空文字列の場合は、長さ0の配列を返します。
686         *
687         * @og.rev 6.8.5.0 (2018/01/09) 新規追加
688         *
689         * @param       vals    double配列に変換する元の文字列配列
690         * @return  指定された文字列配列に対するdoubleに変換された値配列
691         * @og.rtnNotNull
692         */
693        protected final double[][] str2dblVals( final String[][] vals ) {
694                if( vals == null || vals.length == 0 || vals[0] == null || vals[0].length == 0 ) {
695                        return new double[0][0];
696                }
697
698                final int rowLen = vals.length;
699                final int colLen = vals[0].length;
700
701                final double[][] dbls = new double[rowLen][colLen];
702
703                for( int row=0; row<rowLen; row++ ) {
704                        for( int col=0; col<colLen; col++ ) {
705                                dbls[row][col] = str2dbl( vals[row][col] );
706                        }
707                }
708
709                return dbls;
710        }
711
712        /**
713         * テーブルのカラム名の一覧を配列形式で返します。
714         *
715         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
716         *
717         * @return テーブルのカラム名配列
718         */
719        protected final String[] lineKeys() {
720                if( table == null ) {
721                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
722                        final String errMsg = "配列型テーブルモデルがセットされていないため、#lineKeys()メソッドはできません。" ;
723                        throw new OgRuntimeException( errMsg );
724                }
725                else {
726                        return table.getNames();
727                }
728        }
729
730        /**
731         * テーブルにカラムが存在しているかを返します。
732         *
733         * @og.rev 5.2.0.0 (2010/09/01)
734         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
735         *
736         * @param clm カラム名
737         *
738         * @return 存在している場合true、存在していない場合false
739         */
740        protected final boolean isLine( final String clm ) {
741                if( table == null ) {
742                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
743                        final String errMsg = "配列型テーブルモデルがセットされていないため、#isLine( String )メソッドはできません。"     + CR
744                                                        + "   isLine( " + clm + " );"   + CR ;
745                        throw new OgRuntimeException( errMsg );
746                }
747                return table.getColumnNo( clm ) >= 0 ;
748        }
749
750        /**
751         * 業務ロジックの戻り値をセットします。
752         *
753         * @param rtn 戻り値
754         */
755        protected final void rtn( final String rtn ) {
756                bizRtn = rtn;
757        }
758
759        /**
760         * 子ロジックを実行します。
761         * 実行する子ロジックの呼び出しは、親クラスと同じソースパス、クラスパスで呼び出しされます。
762         * 子ロジックに渡す引数には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
763         * また、子ロジックの戻り値は、val("SUB_RETURN")で取得することができます。
764         *
765         * @param subLogicName 子ロジック名
766         * @param key キー(CSV形式)
767         * @param val 値(CSV形式)
768         *
769         * @return 処理が正常終了したか
770         */
771        protected final boolean call( final String subLogicName, final String key, final String val ) {
772                return call( subLogicName, key, val, row, table );
773        }
774
775        /**
776         * 子ロジックを実行します。
777         * 実行する子ロジックの呼び出しは、親クラスと同じソースパス、クラスパスで呼び出しされます。
778         * 子ロジックに渡す引数には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
779         * この場合の値は、引数で指定された、配列型テーブルモデルの行に対応する値になります。
780         * また、子ロジックの戻り値は、val("RETURN")で取得することができます。
781         *
782         * @og.rev 5.1.9.0 (2010/08/01) シーケンス対応
783         * @og.rev 5.4.1.0 (2011/11/01) 値にカンマが含まれている場合に正しく動作しないバグを修正
784         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
785         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
786         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
787         *
788         * @param subLogicName 子ロジック名
789         * @param key キー(CSV形式)
790         * @param val 値(CSV形式)
791         * @param rw 行番号(インデックス)
792         * @param tbl 配列型テーブルモデル
793         *
794         * @return 処理が正常終了したか
795         */
796        protected final boolean call( final String subLogicName, final String key, final String val, final int rw, final DataModel<String> tbl ) {
797                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
798                if( loader == null ) {
799                        final String errMsg = "#setLoader(HybsLoader)を先に実行しておいてください。"   + CR
800                                                        + "   subLogicName =" + subLogicName    + CR
801                                                        + "   key =" + key      + CR
802                                                        + "   val =" + val      + CR
803                                                        + "   ArrayTableModel=" + tbl ;
804                        throw new OgRuntimeException( errMsg );
805                }
806
807                final AbstractBizLogic subLogic = (AbstractBizLogic)loader.newInstance( subLogicName );
808
809                if( subLogic.isRequireTable() ) {
810                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
811                        final String errMsg = "このクラスは、外部からテーブルモデルをセットする必要があるため、子ロジックとして呼び出すことはできません。" + CR
812                                                        + "  [クラス名=" + subLogic.getClass().getName() + "]"      + CR
813                                                        + "   subLogicName =" + subLogicName
814                                                        + "   key =[" + key + "]"
815                                                        + "   val =[" + val + "]" + CR ;
816                        throw new OgRuntimeException( errMsg );
817                }
818
819                subLogic.setTransaction( tran );
820                subLogic.setLoader( loader );
821                subLogic.setKeys( StringUtil.csv2Array( key ) );
822                // 5.4.1.0 (2011/11/01) 値にカンマが含まれている場合に正しく動作しないバグを修正
823                String[] vals = StringUtil.csv2Array( val );
824                for( int i=0; i<vals.length; i++ ) {
825                        vals[i] = replaceParam( vals[i], rw, tbl );
826                }
827                subLogic.setVals( vals );
828                subLogic.setUserId( variableMap.get( "CON.USERID" ) );
829                subLogic.setParentPgId( variableMap.get( "CON.PGID" ) );
830                if( debugFlag ) {
831                        subLogic.setDebug();
832                }
833
834                final boolean rtn;                                              // 6.3.9.0 (2015/11/06) Found 'DU'-anomaly for variable(PMD)
835                try {
836                        rtn = subLogic.exec();
837                }
838                catch( final Throwable th ) {
839                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
840                        final String errMsg = "子ロジックの呼び出しでエラーが発生しました。" + CR
841                                                        + "   subLogicName =" + subLogicName  + CR
842                                                        + "   key =[" + key + "]"
843                                                        + "   val =[" + val + "]" + CR ;
844                        throw new OgRuntimeException( errMsg ,th );
845                }
846                variableMap.put( "RETURN", subLogic.getReturn() );
847
848                if( debugFlag ) { debug( subLogic.getDebugMsg() ); }
849
850                final ErrMsg[] errs = subLogic.getErrMsg().toArray();
851                if( errs.length > 0 ) {
852                        final ErrorMessage errMsgTmp = new ErrorMessage();
853                        for( int i=0; i<errs.length; i++ ) {
854                                errMsgTmp.addMessage( errs[i].copy( rw ) );
855                        }
856                        errMsg.append( errMsgTmp );
857                }
858
859                return rtn;
860        }
861
862        /**
863         * SQLを実行します。
864         * SQL文には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
865         * select文を発行した場合、その結果セットは、var(カラム名)で取得することができます。
866         * 2行以上が返された場合でも、1行目のみが登録されます。
867         * また、検索件数、更新件数については、var("SQL_ROWCOUNT")で取得することができます。
868         *
869         * @param sq SQL文字列
870         */
871        protected final void sql( final String sq ) {
872                sql( sq, row, table );
873        }
874
875        /**
876         * SQLを実行します。
877         * SQL文には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
878         * [XXXX]形式の変数の置き換えには、引数で指定された配列型テーブルモデルの行が使用されます。
879         * select文を発行した場合、その結果セットは、var(カラム名)で取得することができます。
880         * 2行以上が返された場合でも、1行目のみが登録されます。
881         * また、検索件数、更新件数については、var("SQL_ROWCOUNT")で取得することができます。
882         *
883         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
884         *
885         * @param sq SQL文字列
886         * @param rw 行番号(インデックス)
887         * @param tbl 配列型テーブルモデル
888         */
889        protected final void sql( final String sq, final int rw, final DataModel<String> tbl ) {
890                final DataModel<String> tbl2 = execSQL( sq, rw, tbl );
891
892                if( tbl2 != null && tbl2.getRowCount() > 0 ) {
893                        final String[] names = tbl2.getNames();
894                        final String[] vals = tbl2.getValues( 0 );
895                        for( int i=0; i<names.length; i++ ) {
896                                variableMap.put( names[i], vals[i] );
897                        }
898                }
899        }
900
901        /**
902         * シーケンス名よりシーケンスオブジェクトを検索し、次の値を取り出します。
903         * DBに対するシーケンスオブジェクトは予め作成されている必要があります。
904         *
905         * また、MySQLの場合は、シーケンスオブジェクトが実装されていないため、
906         * 内部的には、引数のシーケンス名と同じ名前のテーブルから、Integer型の
907         * "SEQID"という項目名を検索することにより、シーケンスをエミュレートしています。
908         *
909         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
910         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
911         *
912         * @param seqName       シーケンス名
913         *
914         * @return シーケンス番号
915         * @see org.opengion.fukurou.db.DBFunctionName#getSequence(String,Transaction)
916         */
917        protected final int seq( final String seqName ) {
918                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
919                if( dbName == null ) {
920                        final String errMsg = "#exec()を先に実行しておいてください。"  + CR
921                                                        + "   seqName =" + seqName ;
922                        throw new OgRuntimeException( errMsg );
923                }
924
925                return dbName.getSequence( seqName, tran );
926        }
927
928        /**
929         * SQLを実行します。
930         *
931         * @param sq SQL文字列
932         * @param rw 行番号(インデックス)
933         * @param tbl 配列型テーブルモデル
934         *
935         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
936         *
937         * @return 結果セット(配列型テーブルモデル)
938         *
939         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
940         * @og.rev 5.1.8.0 (2010/07/01) column名は大文字化し、項目名の取得は#getColumnLabel()で行う。(PotgreSQL対応&バグ修正)
941         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)、setNull 対応
942         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
943         * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述。
944         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
945         * @og.rev 6.9.3.0 (2018/03/26) ミス修正(検索件数のところを、フェッチ件数を取得していた)
946         * @og.rev 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズを設定。
947         */
948        private DataModel<String> execSQL( final String sq, final int rw, final DataModel<String> tbl ) {
949                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
950                if( conn == null ) {
951                        final String errMsg = "#setTransaction(Transaction)を先に実行しておいてください。"     + CR
952                                                        + "   sql =" + sq               + CR
953                                                        + "   ArrayTableModel=" + tbl ;
954                        throw new OgRuntimeException( errMsg );
955                }
956
957                String sql = replaceParam( sq, false ); // [XXXX]の変換はここでは行わない。
958                Formatter format = null ;
959                if( tbl != null && sql.indexOf( '[' ) >= 0 ) {
960                        format = getFormatter( sql, tbl );
961                        sql = format.getQueryFormatString();
962                }
963
964                DataModel<String>       tbl2    = null;
965                // 6.4.2.1 (2016/02/05) try-with-resources 文
966                try( PreparedStatement pstmt = conn.prepareStatement( sql ) ) {
967                        if( tbl != null && format != null ) {
968                                final int[] clmNo = format.getClmNos();
969
970                                // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
971                                if( useParamMetaData ) {
972                                        final ParameterMetaData pMeta = pstmt.getParameterMetaData();
973                                        for( int i=0; i<clmNo.length; i++ ) {
974                                                final int type = pMeta.getParameterType( i+1 );
975                                                // 5.3.8.0 (2011/08/01) setNull 対応
976                                                final String val = tbl.getValue( rw, clmNo[i] );
977                                                if( val == null || val.isEmpty() ) {
978                                                        pstmt.setNull( i+1, type );
979                                                }
980                                                else {
981                                                        pstmt.setObject( i+1, val, type );
982                                                }
983                                        }
984                                }
985                                else {
986                                        for( int i=0; i<clmNo.length; i++ ) {
987                                                pstmt.setObject( i+1, tbl.getValue( rw, clmNo[i] ) );
988                                        }
989                                }
990                        }
991                        final boolean status = pstmt.execute();
992                        // 6.4.2.1 (2016/02/05) try-with-resources 文
993                        try( ResultSet result = pstmt.getResultSet() ) {
994                                if( status ) {
995                                        result.setFetchSize( DB_FETCH_SIZE );                           // 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ
996
997                                        final ResultSetMetaData metaData = result.getMetaData();
998                                        final int cols = metaData.getColumnCount();
999
1000                                        String[] names = new String[cols];
1001                                        for( int i=0; i<cols; i++ ) {
1002                                                // 5.1.8.0 (2010/07/01) column名は大文字化し、項目名の取得は#getColumnLabel()で行う。(PotgreSQL対応&バグ修正)
1003                                                names[i] = metaData.getColumnLabel( i+1 ).toUpperCase( Locale.JAPAN );
1004                                        }
1005
1006                                        final String[][] tblVals = DBUtil.resultToArray( result, false );
1007                                        tbl2 = new ArrayTableModel( names, tblVals );
1008
1009//                                      variableMap.put( "SQL_ROWCOUNT", String.valueOf( pstmt.getFetchSize() ) );
1010                                        variableMap.put( "SQL_ROWCOUNT", String.valueOf( tbl2.getRowCount() ) );                        // 6.9.3.0 (2018/03/26) ミス修正
1011                                }
1012                                else {
1013                                        variableMap.put( "SQL_ROWCOUNT", String.valueOf( pstmt.getUpdateCount() ) );
1014                                }
1015                        }
1016                }
1017                catch( final SQLException ex ) {                // catch は、close() されてから呼ばれます。
1018                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1019                        final String errMsg = "配列型テーブルモデルの生成に失敗しました。"   + CR
1020                                                        + "   sql =" + sql              + CR
1021                                                        + "   ArrayTableModel=" + tbl ;
1022                        throw new OgRuntimeException( errMsg,ex );
1023                }
1024                return tbl2;
1025        }
1026
1027        /**
1028         * エラーメッセージを追加します。
1029         * エラーメッセージの引数には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
1030         *
1031         * @param kekka エラーレベル
1032         * @param id エラーメッセージID
1033         * @param args エラーメッセージパラメーター
1034         */
1035        protected final void error( final int kekka, final String id, final String... args ) {
1036                error( row, kekka, id, args );
1037        }
1038
1039        /**
1040         * 行指定でエラーメッセージを追加します。
1041         * エラーメッセージの引数には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
1042         *
1043         * @param rw 行番号(インデックス)
1044         * @param kekka エラーレベル
1045         * @param id エラーメッセージID
1046         * @param args エラーメッセージパラメーター
1047         */
1048        protected final void error( final int rw, final int kekka, final String id, final String... args ) {
1049                errMsg.addMessage( rw, kekka, id, replaceParam( args ) );
1050        }
1051
1052        /**
1053         * パラメーターの必須チェックを行います。
1054         * キーは、CSV形式で複数指定することができます。
1055         *
1056         * @param cs カラム(CSV形式)
1057         *
1058         * @return エラーが発生した場合はfalse、それ以外はtrue
1059         */
1060        protected final boolean must( final String cs ) {
1061                if( cs == null || cs.isEmpty() ) {
1062                        return true;
1063                }
1064
1065                final String[] clms = StringUtil.csv2Array( cs );
1066                for( int i=0; i<clms.length; i++ ) {
1067                        final String val = variableMap.get( clms[i] );
1068                        if( val == null || val.isEmpty() ) {
1069//                              error( 2, "ERR0012", "{#" + clms[i] + "}" );
1070                                error( NG, "ERR0012", "{#" + clms[i] + "}" );                   // 7.2.9.5 (2020/11/28)
1071                                return false ;
1072                        }
1073                }
1074                return true;
1075        }
1076
1077        /**
1078         * マスタチェックを行います。
1079         *
1080         * @og.rev 5.6.3.1 (2013/04/05) isErrThrow 引数を追加
1081         *
1082         * @see #exist(String, String, String, String, String, String)
1083         * @param type エラーチェックのタイプ
1084         * @param tblId テーブル名
1085         * @param ns カラム(CSV形式)
1086         * @param vs 値(CSV形式)
1087         *
1088         * @return エラーが発生した場合はfalse、それ以外はtrue
1089         */
1090        protected final boolean exist( final String type, final String tblId, final String ns, final String vs ) {
1091                return exist( type, tblId, ns, vs, null, null,true );
1092        }
1093
1094        /**
1095         * マスタチェックを行います。
1096         *
1097         * 引数に指定されたテーブル名、及び条件句を生成するためのカラム、値から
1098         * 件数を取得し、typeに応じて件数チェックを行います。
1099         * (カラム、値には、CSV形式で複数指定することができます)
1100         *  type=true  存在する場合true  存在しない場合false
1101         *  type=false 存在する場合false 存在しない場合true
1102         *  type=one   1件以内    true  2件以上     false
1103         *
1104         * 必須チェックの引数には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
1105         *
1106         * また、固定値カラム、値にも条件となるカラム及び値を指定することができますが、
1107         * ここで指定されたカラムは、エラーメッセージ表示時にカラム、値が画面に表示されません。
1108         *
1109         * @og.rev 5.6.3.1 (2013/04/05) isErrThrow 引数を追加
1110         *
1111         * @param type エラーチェックのタイプ
1112         * @param tblId テーブル名
1113         * @param ns カラム(CSV形式)
1114         * @param vs 値(CSV形式)
1115         * @param conNs 固定値カラム(CSV形式)
1116         * @param conVs 固定値(CSV形式)
1117         *
1118         * @return エラーが発生した場合はfalse、それ以外はtrue
1119         */
1120        protected final boolean exist( final String type, final String tblId
1121                        , final String ns, final String vs, final String conNs, final String conVs ) {
1122                return exist( type, tblId, ns, vs, conNs, conVs,true );
1123        }
1124
1125        /**
1126         * マスタチェックを行います。
1127         * 引数に指定されたテーブル名、及び条件句を生成するためのカラム、値から
1128         * 件数を取得し、typeに応じて件数チェックを行います。
1129         * (カラム、値には、CSV形式で複数指定することができます)
1130         *  type=true  存在する場合true  存在しない場合false
1131         *  type=false 存在する場合false 存在しない場合true
1132         *  type=one   1件以内    true  2件以上     false
1133         *
1134         * 必須チェックの引数には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
1135         *
1136         * また、固定値カラム、値にも条件となるカラム及び値を指定することができますが、
1137         * ここで指定されたカラムは、エラーメッセージ表示時にカラム、値が画面に表示されません。
1138         *
1139         * isErrThrow は、エラーが発生した場合に、エラーメッセージ(ErrorMessage)に書き込むかどうかを指定します。
1140         * 基本は、互換性を考慮し、true(書き込む)です。
1141         * false にするケースは、存在チェックを行い、あれば更新、なければ追加 など後続処理を行いたい場合に使います。
1142         *
1143         * @og.rev 5.6.3.1 (2013/04/05) isErrThrow 引数を追加
1144         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1145         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
1146         *
1147         * @param type エラーチェックのタイプ
1148         * @param tblId テーブル名
1149         * @param ns カラム(CSV形式)
1150         * @param vs 値(CSV形式)
1151         * @param conNs 固定値カラム(CSV形式)
1152         * @param conVs 固定値(CSV形式)
1153         * @param isErrThrow 判定結果がfalseの場合に、error関数を呼ぶ場合は、true。呼ばない場合は、falseをセットします。
1154         *
1155         * @return エラーが発生した場合はfalse、それ以外はtrue
1156         */
1157        protected final boolean exist( final String type, final String tblId
1158                        , final String ns, final String vs, final String conNs, final String conVs, final boolean isErrThrow ) {
1159                if( ns == null || ns.isEmpty() || vs == null || vs.isEmpty() ) {
1160                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1161                        final String errMsg = "カラム又は、値にnullは指定できません。"   + CR
1162                                                        + "   ns =[" + ns + "]"
1163                                                        + "   vs =[" + vs + "]" ;
1164                        throw new OgRuntimeException( errMsg );
1165                }
1166
1167                final String namesStr   = ns + ( conNs == null || conNs.isEmpty() ? "" : "," + conNs );
1168                final String[] namesArr = StringUtil.csv2Array( namesStr );
1169                final String valsStr    = vs + ( conVs == null || conVs.isEmpty() ? "" : "," + conVs );
1170                final String[] valsArr  = StringUtil.csv2Array( valsStr );
1171                if( namesArr.length != valsArr.length ) {
1172                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1173                        final String errMsg = "カラムと値の個数が異なります。"                 + CR
1174                                                        + "   names = [" + namesStr     + "]"   + CR
1175                                                        + "   vals  = [" + valsStr      + "]";
1176                        throw new OgRuntimeException( errMsg );
1177                }
1178
1179                final StringBuilder sb = new StringBuilder( BUFFER_MIDDLE );
1180                sb.append( "select count(*) CNT from " ).append( tblId );
1181                for( int i=0 ;i<namesArr.length; i++ ) {
1182                        if( i==0 )      { sb.append( " where " ); }
1183                        else            { sb.append( " and " ); }
1184                        sb.append( namesArr[i] ).append( " = " ).append( valsArr[i] );
1185                }
1186
1187                int count = 0;
1188                final DataModel<String> tbl2 = execSQL( sb.toString(), row, table );            // 6.7.9.1 (2017/05/19)
1189                if( tbl2 != null && tbl2.getRowCount() >= 0 ) {
1190                        count = Integer.parseInt( tbl2.getValues( 0 )[0] );                     // 6.0.2.4 (2014/10/17) メソッド間違い
1191                }
1192
1193                final String repVals = replaceParam( vs );
1194                if( "true".equalsIgnoreCase( type ) ) {
1195                        // ERR0025=データ未登録エラー。キー={0}、値={1} のデータは、存在していません。
1196                        if( count <= 0 ) {
1197                                if( isErrThrow ) { error( NG, "ERR0025", "{#" + ns + "}", repVals ); }  // 5.6.3.1 (2013/04/05)
1198                                return false;
1199                        }
1200                }
1201                else if( "false".equalsIgnoreCase( type ) ) {
1202                        // ERR0026=データ登録済みエラー。キー={0}、値={1} のデータは、すでに存在しています。
1203                        if( count > 0 ) {
1204                                if( isErrThrow ) { error( NG, "ERR0026", "{#" + ns + "}", repVals ); }  // 5.6.3.1 (2013/04/05)
1205                                return false;
1206                        }
1207                }
1208                else if( "one".equalsIgnoreCase( type ) ) {
1209                        // ERR0027=データ2重登録エラー。キー={0}、値={1} のデータは、重複して存在しています。
1210                        if( count > 1 ) {
1211                                if( isErrThrow ) { error( NG, "ERR0027", "{#" + ns + "}", repVals ); }  // 5.6.3.1 (2013/04/05)
1212                                return false;
1213                        }
1214                }
1215                else {
1216                        // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1217                        final String errMsg = "typeは、true、false、oneのいずれかで指定する必要があります。"  + CR
1218                                                        + "   type = [" + type  + "]";
1219                        throw new OgRuntimeException( errMsg );
1220                }
1221                return true;
1222        }
1223
1224        /**
1225         * 引数に指定されたキー、値をマップ形式に変換します。
1226         *
1227         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
1228         * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1229         */
1230        private void makeParamMap() {
1231                if( keys != null && vals != null ) {
1232                        if( keys.length == vals.length ) {
1233                                for( int i=0; i<keys.length; i++ ) {
1234                                        variableMap.put( keys[i], vals[i] );
1235                                }
1236                        }
1237                        else {
1238                                // 5.6.7.0 (2013/07/27) Exception を throw するとき、一旦、errMsg 変数にセットします。
1239                                final String errMsg = "keysとvalsの個数が異なります。"             + CR
1240                                                        + "   keys   =" + Arrays.toString( keys )               + CR
1241                                                        + "   vals   =" + Arrays.toString( vals ) ;
1242                                throw new OgRuntimeException( errMsg );
1243                        }
1244                }
1245
1246                final String ymdh = DateSet.getDate( "yyyyMMddHHmmss" );                // 5.5.7.2 (2012/10/09) HybsDateUtil を利用
1247                variableMap.put( "CON.YMDH", ymdh );
1248                variableMap.put( "CON.YMD", ymdh.substring( 0,8 ) );
1249                variableMap.put( "CON.HMS", ymdh.substring( 8 ) );
1250
1251                variableMap.put( "CON.PGID", this.getClass().getSimpleName() );
1252        }
1253
1254        /**
1255         * {&#064;XXXX}形式及び[XXXX]形式の文字列配列の置き換えを行います。
1256         *
1257         * @og.rev 6.2.2.0 (2015/03/27) #replaceParam( String[] , int , ArrayTableModel )  廃止に伴う処置
1258         *
1259         * @param str 置き換え対象の配列
1260         *
1261         * @return 置き換え結果の文字列
1262         */
1263        private String[] replaceParam( final String[] str ) {
1264                for( int i=0; i<str.length; i++ ) {
1265                        str[i] = replaceParam( str[i], row, table );
1266                }
1267                return str;
1268        }
1269
1270        /**
1271         * {&#064;XXXX}形式及び[XXXX]形式の文字列の置き換えを行います。
1272         *
1273         * @param str 置き換え対象の文字列
1274         *
1275         * @return 置き換え結果の文字列
1276         */
1277        private String replaceParam( final String str ) {
1278                return replaceParam( str, row, table );
1279        }
1280
1281        /**
1282         * {&#064;XXXX}形式及び[XXXX]形式の文字列の置き換えを行います。
1283         * isRepTableにfalseを指定した場合、Formatterによる[XXXX]変換は行われません。
1284         * (SQLの変換の場合は、PreparedStatementで処理させるため、[XXXX]の変換は行わない。)
1285         *
1286         * @param str 置き換え対象の文字列
1287         * @param isRepTable Formatterによる[XXXX]変換を行うか
1288         *
1289         * @return 置き換え結果の文字列
1290         */
1291        private String replaceParam( final String str, final boolean isRepTable ) {
1292                return isRepTable ? replaceParam( str, row, table) : replaceParam( str, 0, null ) ;
1293        }
1294
1295        /**
1296         * {&#064;XXXX}形式及び[XXXX]形式の文字列の置き換えを行います。
1297         * [XXXX]形式の置き換えには、引数で指定された配列型テーブルモデル、行番号(インデックス)を使用します。
1298         *
1299         * @og.rev 5.1.8.0 (2010/07/01) 引数チェック漏れ対応
1300         * @og.rev 5.3.9.0 (2011/09/01) nullが連続する場合にゼロストリングに置き換えられないバグを修正
1301         * @og.rev 6.4.3.2 (2016/02/19) Formatterを、値が null の場合は、ゼロ文字列を設定する。
1302         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
1303         *
1304         * @param str 置き換え対象の文字列
1305         * @param rw 行番号(インデックス)
1306         * @param tbl 配列型テーブルモデル
1307         *
1308         * @return 置き換え結果の文字列
1309         */
1310        private String replaceParam( final String str, final int rw, final DataModel<String> tbl ) {
1311                // 5.1.8.0 (2010/07/01) 引数チェック漏れ対応
1312                if( str == null || str.isEmpty() ) { return ""; }
1313
1314                String rtn = str;
1315
1316                // {@XXXX}の変換
1317                if( !variableMap.isEmpty() && rtn.indexOf( "{@" ) >= 0 ) {              // 6.1.1.0 (2015/01/17) refactoring
1318                        final SystemParameter sysParam = getSysParam( rtn );
1319                        rtn = sysParam.replace( variableMap );
1320                }
1321
1322                // [XXXX]の変換
1323                if( tbl != null && rtn.indexOf( '[' ) >= 0 ) {
1324                        final Formatter format = getFormatter( rtn, tbl );
1325                        rtn = format.getFormatString( rw );
1326                }
1327
1328                return rtn;
1329        }
1330
1331        /**
1332         * [XXXX]変換を行うためのFormatterを取得します。
1333         *
1334         * @og.rev 6.4.3.4 (2016/03/11) Formatterに新しいコンストラクターを追加する。
1335         * @og.rev 6.4.3.4 (2016/03/11) Map#computeIfAbsent で対応する。
1336         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
1337         *
1338         * @param str 変換文字列
1339         * @param tbl 配列型テーブルモデル
1340         *
1341         * @return Formatterオブジェクト
1342         */
1343        private Formatter getFormatter( final String str, final DataModel<String> tbl ) {
1344                // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
1345                final String key = str + tbl.toString();
1346                return formatMap.computeIfAbsent( key , k -> new Formatter( tbl,str ) );
1347        }
1348
1349        /**
1350         * {&#064;XXXX}変換を行うためのSystemParameterオブジェクトを取得します。
1351         *
1352         * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加
1353         *
1354         * @param str 変換文字列
1355         *
1356         * @return SystemParameterオブジェクト
1357         */
1358        private SystemParameter getSysParam( final String str ) {
1359                // 6.4.3.3 (2016/03/04) キーが null のときも、SystemParameter オブジェクトを構築しているので、
1360                // それも合わせて、Mapで管理するようにします。
1361                final String key = str == null ? "NULL" : str ;
1362                // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
1363                return sysParamMap.computeIfAbsent( key , k -> new SystemParameter( k ) );
1364        }
1365
1366        /**
1367         * 検索SQLを実行し、結果を配列型テーブルモデルとして返します。
1368         * SQL文には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
1369         * また、検索件数については、var("SQL_ROWCOUNT")で取得することができます。
1370         *
1371         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
1372         *
1373         * @param sq SQL文
1374         *
1375         * @return 配列型テーブルモデル
1376         */
1377        protected final DataModel<String> createTableBySql( final String sq ) {
1378                return createTableBySql( sq, row, table );
1379        }
1380
1381        /**
1382         * 検索SQLを実行し、結果を配列型テーブルモデルとして返します。
1383         * SQL文には、{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます。
1384         * [XXXX]形式の変数の置き換えには、引数で指定された配列型テーブルモデルの行が使用されます。
1385         * また、検索件数については、var("SQL_ROWCOUNT")で取得することができます。
1386         *
1387         * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更。
1388         *
1389         * @param sq SQL文
1390         * @param rw 行番号(インデックス)
1391         * @param tbl 配列型テーブルモデル
1392         *
1393         * @return 配列型テーブルモデル
1394         */
1395        protected final DataModel<String> createTableBySql( final String sq, final int rw, final DataModel<String> tbl ) {
1396                return execSQL( sq, rw, tbl );
1397        }
1398
1399        /**
1400         * 変数に関連付けた値を、返します。
1401         * これは、BizLogicから、呼び出し元のJSPに、RETURN 変数以外の {&#064;XXXX} パラメータを返します。
1402         * 既存のアトリビュートがあれば、上書きされます。
1403         *
1404         * @og.rev 6.9.9.0 (2018/08/20) 戻り値を返せるようにします。
1405         *
1406         * @param key キー
1407         * @param val 値
1408         *
1409         */
1410        protected final void setRtnMap( final String key, final String val ) {
1411                if( key != null && val != null ) {                      // ConcurrentMap なので。
1412                        rtnMap.put( key, val );
1413                }
1414        }
1415
1416        /**
1417         * 変数に関連付けた値を、返します。
1418         * これは、BizLogicから、呼び出し元のJSPに、RETURN 変数以外の {&#064;XXXX} パラメータを返します。
1419         * 既存のアトリビュートがあれば、上書きされます。
1420         *
1421         * @og.rev 6.9.9.0 (2018/08/20) 戻り値を返せるようにします。
1422         *
1423         * @return 内部マップオブジェクト
1424         */
1425        protected final Map<String,String> getReturnMap() {
1426                return rtnMap;
1427        }
1428}