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