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