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