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