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