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