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