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.hayabusa.taglib;
017
018import org.opengion.fukurou.db.Transaction;
019import org.opengion.fukurou.db.ResultSetValue;                                          // 6.0.4.0 (2014/11/28)
020import org.opengion.fukurou.util.ErrorMessage;
021import org.opengion.fukurou.util.FileUtil;
022import org.opengion.fukurou.util.StringUtil;
023import org.opengion.fukurou.system.Closer ;
024import org.opengion.fukurou.util.ToString;                                                      // 6.1.1.0 (2015/01/17)
025
026import static org.opengion.fukurou.util.StringUtil.nval ;
027import static org.opengion.fukurou.system.HybsConst.BR;                         // 6.1.0.0 (2014/12/26) refactoring
028import static org.opengion.fukurou.system.HybsConst.DB_FETCH_SIZE;      // 6.9.4.1 (2018/04/09)
029
030import org.opengion.hayabusa.common.HybsSystem;
031import org.opengion.hayabusa.common.HybsSystemException;
032import org.opengion.hayabusa.resource.GUIInfo;
033import org.opengion.hayabusa.resource.ResourceManager;
034import org.opengion.hayabusa.db.DBErrMsg;
035import org.opengion.hayabusa.io.HybsFileOperationFactory;                       // 8.0.0.1 (2021/10/08)
036// import org.opengion.fukurou.model.FileOperation;                             // 8.0.0.1 (2021/10/08)
037
038import java.sql.Connection;
039import java.sql.Statement;
040import java.sql.CallableStatement;
041import java.sql.ResultSet;
042import java.sql.SQLException;
043import java.sql.Types;
044import java.sql.Array;                                                          // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ) 対応。oracle.sql.ARRAY の置き換え
045import oracle.jdbc.OracleConnection;                            // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ) 対応
046import oracle.jdbc.OracleTypes;                                         // CURSOR が残る
047import oracle.jdbc.OracleCallableStatement;                     // CURSOR が残る
048
049import java.io.File;
050import java.io.PrintWriter;
051import java.io.FileOutputStream;
052import java.io.BufferedOutputStream;                            // 6.0.4.0 (2014/11/28)
053
054import java.io.IOException;
055import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;                       // 6.0.4.0 (2014/11/28)
056import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;        // 6.0.4.0 (2014/11/28)
057
058import java.util.Map;
059
060/**
061 * SELECT文を直接実行して、指定のファイルに出力するタグです。
062 *
063 * 中間の、データ(DBTableModel)を作成しないため、余計なメモリを取らず、
064 * 高速にデータを抜き出すことが可能です。
065 * 一方、抜き出すデータは生データのため、データの再利用等、システム的な
066 * 使用を想定しています。
067 * JDBCErrMsg 形式のPL/SQL をコールして、その検索結果(カーソル)を抜く事もできます。
068 *
069 * ※ このタグは、Transaction タグの対象です。
070 *
071 * @og.formSample
072 * ●形式:<og:directWriteTable filename="[・・・]" ・・・ >SELECT * FROM ZYXX </og:directWriteTable >
073 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します)
074 *
075 * ●Tag定義:
076 *   <og:directWriteTable
077 *       fileURL            【TAG】保存先ディレクトリ名を指定します (初期値:FILE_URL[=filetemp/])
078 *       filename           【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME)
079 *       encode             【TAG】ファイルを作成するときのファイルエンコーディング名をセットします (初期値:FILE_ENCODE[=UnicodeLittle])
080 *       fileAppend         【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[通常モード])
081 *       zip                【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false)
082 *       zipFilename        【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip")
083 *       separator          【TAG】可変長ファイルを作成するときの項目区切り文字をセットします (初期値:TAB_SEPARATOR)
084 *       useHeader          【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true)
085 *       useQuote           【TAG】データをダブルクオートで囲うかどうか指定します(初期値:false)
086 *       useQuoteEscape     【TAG】データ中にダブルクオート文字が含まれる場合、エスケープするかどうか指定します(初期値:true)
087 *       useReturnQuote     【TAG】データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか指定します(初期値:true)
088 *       replaceFrom        【TAG】置換元文字を指定。一文字単位で置換します(初期値:null 置換なし)。
089 *       replaceTo          【TAG】置換先文字を指定。一文字単位で置換します。
090 *       displayMsg         【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します(初期値:VIEW_DISPLAY_MSG[=])
091 *       notfoundMsg        【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])
092 *       fetchSize          【TAG】(通常は使いません)データのフェッチサイズを指定します(初期値:DB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])
093 *       names              【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します
094 *       queryType          【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC})
095 *       dbid               【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)
096 *       useNumber          【TAG】行番号を出力するかどうか(初期値:true)
097 *       quotCheck          【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します(初期値:USE_SQL_INJECTION_CHECK)
098 *       xssCheck           【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_XSS_CHECK[=true])
099 *       useTimeView        【TAG】処理時間を表示する TimeView を表示するかどうかを指定します
100 *                                      (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
101 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
102 *       useLocal           【TAG】システム定数でクラウド設定されていても、クラウド環境を使用しない場合、trueを指定します(初期値:false) 8.0.1.0 (2021/10/29)
103 *       mapObjKey          【TAG】7.0.7.1 (2019/12/24) valueタグのaction=MAPOBJ を使用したラベル変換を行う場合の、MAPOBJキーを指定します。
104 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
105 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
106 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
107 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
108 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
109 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
110 *   >   ... Body ...
111 *   </og:directWriteTable>
112 *
113 * ●使用例
114 *     <og:directWriteTable
115 *         dbid        = "ORCL"               接続データベースID(初期値:DEFAULT)
116 *         separator   = ","                  ファイルの区切り文字(初期値:タブ)
117 *         fileURL     = "{@USER.ID}"    保存先ディレクトリ名
118 *         filename    = "{@filename}"   保存ファイル名
119 *         encode      = "UnicodeLittle"      保存ファイルエンコード名
120 *         useHeader   = "true"               保存ファイルにヘッダーを出力するかどうか
121 *         useQuote    = "false"              データをダブルクオートで囲うかどうか
122 *         useQuoteEscape = "true"            ダブルクオート文字が含まれる場合、エスケープするかどうか
123 *         useReturnQuote = "true"            改行コードが含まれる場合、ダブルクオートで囲うかどうか
124 *         replaceFrom = "',"*%|"        置換元文字を指定。一文字単位で置換します。
125 *         replaceTo   = "’,”*%|"       置換先文字を指定。一文字単位で置換します。
126 *         zip         = "true"               ZIPファイルに圧縮するかどうか
127 *         zipFilename = "Sample.zip"         ZIPファイルのファイル名
128 *         fileAppend  = "true"               ファイルを追加モードで登録するかどうか
129 *         displayMsg  = "MSG0033"            実行後の表示メッセージ
130 *         fetchSize   = "200"                DB検索する場合のフェッチするサイズ
131 *     >
132 *         SELECT * FROM ZYXX
133 *     </og:directWriteTable >
134 *
135 *     <og:directWriteTable
136 *         fileURL     = "{@USER.ID}"    保存先ディレクトリ名
137 *         filename    = "{@filename}"   保存ファイル名
138 *         names       = "AAA,BBB,CCC,・・・"    指定のキーに対応するリクエスト値を ARG_ARRAY にセットします。
139 *         queryType   = "JDBCErrMsg"         JDBCErrMsg 形式のPL/SQL をコールします。
140 *     >
141 *        { call PL/SQL(?,?,?,? ) }
142 *     </og:directWriteTable >
143 *
144 * @og.rev 3.5.6.0 (2004/06/18) 新規作成
145 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)の実行を追加
146 * @og.group ファイル出力
147 *
148 * @version  4.0
149 * @author   Kazuhiko Hasegawa
150 * @since    JDK5.0,
151 */
152public class DirectWriteTableTag extends CommonTagSupport {
153        /** このプログラムのVERSION文字列を設定します。 {@value} */
154        private static final String VERSION = "8.0.1.0 (2021/10/29)" ;
155        private static final long serialVersionUID = 801020211029L ;
156
157        private static final String TAB_SEPARATOR       = "\t" ;
158        private static final String ERR_MSG_ID          = HybsSystem.ERR_MSG_KEY;               // 6.4.1.1 (2016/01/16) errMsgId → ERR_MSG_ID  refactoring
159
160        private final int DB_MAX_QUERY_TIMEOUT          = HybsSystem.sysInt( "DB_MAX_QUERY_TIMEOUT" ) ;
161        private static final String ARG_ARRAY           = "ARG_ARRAY" ;
162        private static final String ERR_MSG                     = "ERR_MSG" ;
163        private static final String ERR_MSG_ARRAY       = "ERR_MSG_ARRAY" ;
164
165//      /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ */
166//      private static final int DB_FETCH_SIZE          = HybsSystem.sysInt( "DB_FETCH_SIZE" ) ;
167
168        // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
169        private String  dbid            ;
170        private String  separator       = TAB_SEPARATOR;        // 項目区切り文字
171        private boolean useHeader       = true;                         // ヘッダーの使用可否
172        private boolean useQuote        ;                                       // 6.0.3.0 (2014/11/13) ダブルクオートで囲うかどうか
173        private boolean useQuoteEscape  = true;                 // 6.0.3.0 (2014/11/13) データ中にダブルクオート文字が含まれる場合、エスケープするかどうか
174        private boolean useReturnQuote  = true;                 // 6.0.3.0 (2014/11/13) データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか
175        private String  fileURL         = HybsSystem.sys( "FILE_URL" );
176        private String  filename        = HybsSystem.sys( "FILE_FILENAME" );    // ファイル名
177        private String  sql                     ;
178        private String  encode          = HybsSystem.sys( "FILE_ENCODE"   );    // ファイルエンコーディング  "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
179        private boolean fileAppend      ;                                       // ファイルをAPPENDモードで出力するか
180        private boolean zip                     ;                                       // ファイルをZIPするか
181        private String  zipFilename     ;                                       // ZIPファイル名
182        private String  displayMsg      = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
183        private String  notfoundMsg     = "MSG0077";            // 対象データはありませんでした。
184        private long    dyStart         ;                                       // 実行時間測定用のDIV要素を出力します。
185        private boolean useTimeView     = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );             // 6.3.6.0 (2015/08/16)
186        private int             fetchSize       = DB_FETCH_SIZE ;       // フェッチする行数(初期値:1001)   6.9.3.0 (2018/03/26) 初期値を100→HybsConst.DB_FETCH_SIZE に変更
187        private boolean useNumber       = true;                         // 5.5.7.1(2012/10/05) 行番号出力
188
189        private String  replaceFrom     ;                                       // 6.0.3.0 (2014/11/13) 置換元文字を指定
190        private String  replaceTo       ;                                       // 6.0.3.0 (2014/11/13) 置換先文字を指定
191
192        private boolean quotCheck       = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );      // 6.2.2.0 (2015/03/27)
193        private boolean xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );                        // 6.2.2.0 (2015/03/27)
194
195        // 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
196        private boolean queryType       = true;                         // ノーマルは、true/ JDBCErrMsg の時は、false
197        private String  names           ;                                       // 指定のリクエスト変数を、ARG_ARRAY にセットします。
198        private int             errCode         = ErrorMessage.OK;
199        private transient ErrorMessage errMessage       ;
200        private boolean useSLabel       ;                                       // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
201        private boolean useLocal        ;                                       // 8.0.1.0 (2021/10/29) クラウド設定を使用しない場合は、true
202        private String  mapObjKey       ;                                       // 7.0.7.1 (2019/12/24) valueタグのaction=MAPOBJ を使用したラベル変換を行う場合の、MAPOBJキーを指定します。
203
204        /**
205         * デフォルトコンストラクター
206         *
207         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
208         */
209        public DirectWriteTableTag() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
210
211        /**
212         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
213         *
214         * @og.rev 6.0.3.0 (2014/11/13) 置換元文字,置換先文字のチェック
215         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
216         * @og.rev 6.4.8.1 (2016/07/02) xssCheckを、doStartTag に移動
217         *
218         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
219         */
220        @Override
221        public int doStartTag() {
222                // 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
223                // 反転注意、if ロジック統合
224                if( useTag() ) {
225                        dyStart = System.currentTimeMillis();           // 時間測定用
226
227                        useXssCheck( xssCheck );                        // 6.4.8.1 (2016/07/02)
228
229                        // 6.0.3.0 (2014/11/13) 置換元文字,置換先文字を指定
230                        if( ( replaceFrom != null || replaceTo != null ) &&
231                                ( replaceFrom == null || replaceTo == null || replaceFrom.length() != replaceTo.length() ) ) {
232                                        final String errMsg = "置換元文字と置換先文字の文字数が異なります。" + CR
233                                                                + " replaceFrom=[" + replaceFrom + "] , replaceTo=[" + replaceTo + "]"
234                                                                + CR ;
235                                        throw new HybsSystemException( errMsg );
236                        }
237
238                        return EVAL_BODY_BUFFERED ;     // Body を評価する。( extends BodyTagSupport 時)
239                }
240                else {
241                        return SKIP_BODY;
242                }
243        }
244
245        /**
246         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
247         *
248         * @og.rev 3.8.6.3 (2006/11/30) SQL 文の前後のスペースを取り除きます。
249         * @og.rev 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
250         * @og.rev 6.4.8.1 (2016/07/02) xssCheckを、doStartTag に移動
251         *
252         * @return      後続処理の指示(SKIP_BODY)
253         */
254        @Override
255        public int doAfterBody() {
256                // 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
257                useQuotCheck( quotCheck );
258
259                sql = getBodyString();
260                if( sql == null || sql.isEmpty() ) {
261                        final String errMsg = "BODY 部の検索用 Select文は、必須です。";
262                        throw new HybsSystemException( errMsg );
263                }
264                sql = sql.trim();
265                return SKIP_BODY ;                              // Body を評価しない
266        }
267
268        /**
269         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
270         *
271         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
272         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel )
273         * @og.rev 6.0.4.0 (2014/11/28) Zip処理を、ZipOutputStream → ZipArchiveOutputStream に変更
274         * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
275         * @og.rev 6.3.8.0 (2015/09/11) FileUtil#getPrintWriter( OutputStream,String ) を使用。
276         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
277         * @og.rev 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
278         *
279         * @return      後続処理の指示
280         */
281        @Override
282        public int doEndTag() {
283                debugPrint();           // 4.0.0 (2005/02/28)
284                if( !useTag() ) { return EVAL_PAGE ; }                  // 6.3.4.0 (2015/08/01)
285
286                PrintWriter pw = null;
287                final int executeCount;
288                final File localFile = makeLocalFile();                 // 8.0.0.2 (2021/10/15)
289                try {
290                        if( zip ) {
291//                              // 8.0.0.2 (2021/10/15) zip も考慮して makeLocalFile() で対応する。
292//                              final String directory = HybsSystem.url2dir( fileURL );
293
294//                              if( zipFilename == null ) { zipFilename = filename + ".zip"; }
295//                              final File zipFile = new File( directory,zipFilename );         // 8.0.0.1 (2021/10/08)
296                                ZipArchiveOutputStream gzip = null;                     // 6.0.4.0 (2014/11/28)
297                                try {
298                                        // 6.0.4.0 (2014/11/28) Zip処理を、ZipOutputStream → ZipArchiveOutputStream に変更
299                                        gzip = new ZipArchiveOutputStream(
300                                                                new BufferedOutputStream (
301                                                                        new FileOutputStream (
302//                                                                              new File( directory,zipFilename ))));   // 6.0.4.0 (2014/11/28)
303                                                                                localFile )));                                                  // 8.0.0.2 (2021/10/15)
304                                        gzip.setEncoding( "Windows-31J" );
305                                        gzip.putArchiveEntry( new ZipArchiveEntry( filename ) );
306                                        // 6.0.4.0 (2014/11/28) ファイルのencode を指定できるようにする。
307                                        // 6.3.8.0 (2015/09/11) FileUtil#getPrintWriter( OutputStream,String ) を使用。
308                                        pw = FileUtil.getPrintWriter( gzip,encode );            // 6.3.8.0 (2015/09/11)
309                                        executeCount = create( pw ) ;
310
311                                        pw.flush();
312                                        gzip.closeArchiveEntry();                               // 6.0.4.0 (2014/11/28)
313                                        gzip.finish() ;
314                                }
315                                finally {
316                                        Closer.ioClose( gzip );         // 4.0.0 (2006/01/31) close 処理時の IOException を無視
317                                }
318
319//                              // 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
320//                              // 5.10.9.0 (2019/03/01) ADD クラウドストレージ利用
321//                              HybsFileOperationFactory.local2cloud( () -> zipFile );
322//                              final FileOperation cloudFile = HybsFileOperationFactory.create( directory,zipFilename );
323//                              if( cloudFile.isCloud() ) {
324//                                      FileUtil.copy( zipFile, cloudFile );
325//                                      zipFile.delete();
326//                              }
327                        }
328                        else {
329//                              pw = getPrintWriter();
330                                pw = getPrintWriter( localFile );               // 8.0.0.2 (2021/10/15)
331                                executeCount = create( pw );
332                        }
333                } catch( final IOException ex ) {
334                        final String errMsg = "Error in DirectWriteTableTag: " + toString();
335                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
336                } finally {
337                        Closer.ioClose( pw );           // 4.0.0 (2006/01/31) close 処理時の IOException を無視
338                }
339
340                // 8.0.0.2 (2021/10/15) ローカルファイルから、クラウドに戻す。
341                HybsFileOperationFactory.local2cloud( useLocal,() -> localFile );
342
343                // 3.6.1.0 (2005/01/05) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
344                setRequestAttribute( "DB.COUNT"   , String.valueOf( executeCount ) );
345                setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
346
347                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
348
349                // 実行件数の表示
350                if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
351                        buf.append( executeCount )
352                                .append( getResource().getLabel( displayMsg ) )
353                                .append( BR );
354                }
355                else if( executeCount == 0 && notfoundMsg != null && notfoundMsg.length() > 0 ) {
356                        buf.append( getResource().getLabel( notfoundMsg ) )
357                                .append( BR );
358                }
359
360                // 3.6.1.0 (2005/01/05) TaglibUtil.makeHTMLErrorTable メソッドを利用
361//              final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
362                final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource(),useSLabel );         // 7.0.7.0 (2019/12/13)
363                if( err != null && err.length() > 0 ) {
364                        buf.append( err );
365                        setSessionAttribute( ERR_MSG_ID,errMessage );
366                }
367                else {
368                        removeSessionAttribute( ERR_MSG_ID );
369                }
370
371                jspPrint( buf.toString() );
372
373                // 3.6.1.0 (2005/01/05) 警告時に停止していましたが、継続処理させます。
374                int rtnCode = EVAL_PAGE;
375                if( errCode >= ErrorMessage.NG )  {     // 異常
376                        rtnCode = SKIP_PAGE;
377                }
378
379                // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録)
380                final long dyTime = System.currentTimeMillis()-dyStart;
381                final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY );
382                if( guiInfo != null ) { guiInfo.addReadCount( executeCount,dyTime,sql ); }
383
384                if( useTimeView ) {             // 6.3.6.0 (2015/08/16)
385                        // 時間測定用の DIV 要素を出力
386                        jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );  // 3.5.6.3 (2004/07/12)
387                }
388                return rtnCode ;
389        }
390
391        /**
392         * タグリブオブジェクトをリリースします。
393         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
394         *
395         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
396         * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
397         * @og.rev 5.5.7.1 (2012/10/05) useNumber追加
398         * @og.rev 6.0.3.0 (2014/11/13) useHeader,useQuote,useQuoteEscape,useReturnQuote,replaceFrom,replaceTo追加
399         * @og.rev 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
400         * @og.rev 6.9.3.0 (2018/03/26) fetchSizeの初期値を100→HybsConst.DB_FETCH_SIZE に変更
401         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
402         * @og.rev 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
403         * @og.rev 8.0.1.0 (2021/10/29) useLocal 属性を追加。
404         */
405        @Override
406        protected void release2() {
407                super.release2();
408                separator       = TAB_SEPARATOR;        // 項目区切り文字
409                fileURL         = HybsSystem.sys( "FILE_URL" );
410                filename        = HybsSystem.sys( "FILE_FILENAME" );    // ファイル名
411                sql                     = null;
412                encode          = HybsSystem.sys( "FILE_ENCODE" );              // ファイルエンコーディング  "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
413                fileAppend      = false;                        // ファイルをAPPENDモードで出力するか
414                zip                     = false;                        // ファイルをZIPするか
415                zipFilename     = null;                         // ZIPファイル名
416                displayMsg      = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
417                notfoundMsg     = "MSG0077";            // 対象データはありませんでした。
418                dbid            = null;
419                fetchSize       = DB_FETCH_SIZE ;       // フェッチする行数(初期値:0 参考にしない)               6.9.3.0 (2018/03/26) 初期値を100→→HybsConst.DB_FETCH_SIZE に変更
420                useTimeView     = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );     // 6.3.6.0 (2015/08/16)
421                queryType       = true;                         // ノーマルは、true/ JDBCErrMsg の時は、false
422                names           = null;                         // 指定のリクエスト変数を、ARG_ARRAY にセットします。
423                errCode         = ErrorMessage.OK;
424                errMessage      = null;
425                useNumber       = true;                         // 5.5.7.1 (2012/10/05)
426                useHeader       = true;                         // ヘッダーの使用可否  … 6.0.3.0 (2014/11/13) 追加
427                useQuote        = false;                        // 6.0.3.0 (2014/11/13) ダブルクオートで囲うかどうか
428                useQuoteEscape  = true;                 // 6.0.3.0 (2014/11/13) データ中にダブルクオート文字が含まれる場合、エスケープするかどうか
429                useReturnQuote  = true;                 // 6.0.3.0 (2014/11/13) データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか
430                replaceFrom     = null;                         // 6.0.3.0 (2014/11/13) 置換元文字を指定
431                replaceTo       = null;                         // 6.0.3.0 (2014/11/13) 置換先文字を指定
432                quotCheck       = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );      // 6.2.2.0 (2015/03/27)
433                xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );                        // 6.2.2.0 (2015/03/27)
434                useSLabel       = false;                        // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
435                useLocal        = false;                        // 8.0.1.0 (2021/10/29) クラウド設定を使用しない場合は、true
436                mapObjKey       = null;                         // 7.0.7.1 (2019/12/24) valueタグのaction=MAPOBJ を使用したラベル変換を行う場合の、MAPOBJキーを指定します。
437        }
438
439        /**
440         * 実オブジェクトを生成して、OutputStream に書き込みます。
441         *
442         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
443         * @og.rev 3.8.6.0 (2006/09/29) ヘッダーにラベルを出力するように修正
444         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
445         * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応
446         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応
447         * @og.rev 5.2.2.0 (2010/11/01) 改行を含む場合は、ダブルクオートを強制的に前後に追加する。
448         * @og.rev 5.2.2.0 (2010/11/01) ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加する。
449         * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し
450         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
451         * @og.rev 5.5.7.1 (2012/10/05) useNumberの追加
452         * @og.rev 6.0.3.0 (2014/11/13) useQuote,useQuoteEscape,useReturnQuote,replaceFrom,replaceToの追加
453         * @og.rev 6.0.3.0 (2014/11/13) ヘッダーとラベルを、指定の separator で出力するように変更します。
454         * @og.rev 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
455         * @og.rev 6.2.0.0 (2015/02/27) データ出力の先頭カンマの判定処理変更
456         * @og.rev 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
457         * @og.rev 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
458         *
459         * @param   outPW 出力先のPrintWriterオブジェクト
460         *
461         * @return      検索件数
462         */
463        @SuppressWarnings(value={"unchecked"})
464        private int create( final PrintWriter outPW )  {
465                final int executeCount;
466                Statement stmt = null;
467                CallableStatement callStmt = null; // 4.3.4.3 (2008/12/22)
468                ResultSet resultSet = null ;
469
470                // 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
471                try( Transaction tran = getTransaction() ) {
472                        final Connection conn = tran.getConnection( dbid );                             // 5.1.9.0 (2010/08/01) Transaction 対応
473                        // 3.6.1.0 (2005/01/05)
474                        if( queryType ) {               // JDBC 通常の SELECT 文
475                                stmt = conn.createStatement();
476                                if( fetchSize > 0 ) { stmt.setFetchSize( fetchSize ); }
477                                resultSet = stmt.executeQuery( sql );
478                        }
479                        else {                                  // PL/SQL Call 文
480                                String[] values = null;
481                                if( names != null ) {
482                                        final String[] nameArray = StringUtil.csv2Array( names );
483                                        values = getRequest( nameArray );
484                                }
485                                callStmt  = conn.prepareCall( sql );
486                                resultSet = executeCall( conn,callStmt,values );                // 5.3.0.0 (2010/12/01)
487                        }
488                        if( resultSet == null ) { return 0; }
489
490                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
491                        final ResultSetValue rsv = new ResultSetValue( resultSet );
492                        final int numberOfColumns =  rsv.getColumnCount();
493
494                        // ヘッダー部の出力
495                        if( useHeader && numberOfColumns > 0 ) {
496                                final StringBuilder headName  = new StringBuilder( BUFFER_MIDDLE );
497                                final StringBuilder headLabel = new StringBuilder( BUFFER_MIDDLE );
498                                if( useNumber ){                                                // 6.0.3.0 (2014/11/13) ヘッダー部の useNumber 対応漏れ
499                                        headName.append(  "#Name" );
500                                        headLabel.append( "#Label" );
501                                }
502
503                                Map<String,String> mapObj = null;               // 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
504                                if( mapObjKey != null ) {
505                                        mapObj = (Map<String,String>)getRequestAttribute( mapObjKey );
506                                }
507
508                                final ResourceManager resource = getResource();
509                                // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
510                                for( int clmNo=0; clmNo<numberOfColumns; clmNo++ ) {
511                                        final String clm = rsv.getColumnName(clmNo);
512                                        if( clmNo > 0 || useNumber ){                                           // 5.5.7.1 (2012/10/05)
513                                                //この場合だけセパレータ出力する。
514                                                headName.append( separator );                                   // 6.0.3.0 (2014/11/13)
515                                                headLabel.append( separator );                                  // 6.0.3.0 (2014/11/13)
516                                        }
517                                        headName.append( clm );                                                         // 6.0.3.0 (2014/11/13)
518//                                      headLabel.append( resource.getLabel( clm ) );           // 6.0.3.0 (2014/11/13)
519                                        // 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
520                                        String lbl = resource.getLabel( clm );
521                                        if( mapObj != null ) {
522                                                lbl = mapObj.getOrDefault( clm,lbl );                   // 7.0.7.1 (2019/12/24) Mapの値を優先して、null なら リソース使用
523                                        }
524                                        headLabel.append( lbl );                                                        // 6.0.3.0 (2014/11/13)
525                                }
526                                outPW.println( headName.toString() );
527                                outPW.println( headLabel.toString() );
528                        }
529
530                        int rowNo = 0;
531                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
532                        while( rsv.next() ) {
533                                // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
534                                if( useNumber ){                                        // 5.5.7.1 (2012/10/05)
535                                        if( useQuote ) { outPW.print( "\"" + rowNo + "\"" ); }  // 行番号
536                                        else {                   outPW.print( rowNo ); }
537                                }
538                                for( int clmNo=0; clmNo<numberOfColumns; clmNo++ ) {
539                                        // 6.0.2.5 (2014/10/31) refactoring:Avoid empty if statements 警告の対応
540                                        if( clmNo > 0 || useNumber ){                                   // 6.2.0.0 (2015/02/27)
541                                                //この場合だけセパレータ出力する。
542                                                outPW.print( separator );
543                                        }
544                                        String sval = replace( rsv.getValue(clmNo) );                           // 禁則文字の置換処理
545                                        if( sval != null && sval.length() > 0 ) {
546                                                // 6.0.3.0 (2014/11/13) データ中にダブルクオート文字が含まれる場合、エスケープするかどうか
547                                                if( useQuoteEscape && sval.indexOf( '"' ) >= 0 ) { sval = sval.replaceAll( "\"" ,"\"\"" ) ; }
548                                                // 6.0.3.0 (2014/11/13) データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか
549                                                if( useQuote || useReturnQuote && sval.indexOf( CR ) >= 0 ) {
550                                                        sval = "\"" + sval + "\"" ;
551                                                }
552                                        }
553                                        else {
554                                                sval = useQuote ? "\"\"" : "" ;
555                                        }
556                                        outPW.print( sval );
557                                }
558                                outPW.println();
559                                rowNo++ ;
560                        }
561                        executeCount = rowNo ;
562                        tran.commit();                          // 6.3.6.1 (2015/08/28)
563                }
564                catch( final SQLException ex ) {                // catch は、close() されてから呼ばれます。
565                        final String errMsg = "データベース処理を実行できませんでした。"
566                                                 + CR + '[' + sql + ']' + CR
567                                                 + "err=[" + ex.getSQLState() + ']'
568                                                 + ex.getMessage();
569                        throw new HybsSystemException( errMsg,ex );
570                }
571                finally {                                               // finally は、close() されてから呼ばれます。
572                        Closer.resultClose( resultSet );
573                        Closer.stmtClose( stmt );
574                        Closer.stmtClose( callStmt );   // 4.3.4.3 (2008/12/22)
575                }
576
577                return executeCount ;
578        }
579
580        /**
581         * ローカルファイルを作成します。
582         *
583         * ディレクトリの存在チェックとなければ作成します。
584         * zipファイルの指定がされて、zipFilenameが指定されていない場合は、filename + ".zip" とします。
585         *
586         * @og.rev 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
587         *
588         * @return      ローカルファイルオブジェクト
589         */
590        private File makeLocalFile() {
591                if( filename == null ) {
592                        final String errMsg = "ファイル名がセットされていません。";
593                        throw new HybsSystemException( errMsg );
594                }
595                final String directory = HybsSystem.url2dir( fileURL );                 // こちらは、ローカル変数でよい。
596
597                // 5.6.1.0 (2013/02/01)
598                final File dir = new File(directory);
599                if( ! dir.exists() && ! dir.mkdirs() ) {
600                        final String errMsg = "ディレクトリの作成に失敗しました。[" + directory + "]";
601                        throw new HybsSystemException( errMsg );
602                }
603
604                final String newFilename ;
605                if( zip ) {
606                        if( zipFilename == null ) { newFilename = filename + ".zip"; }
607                        else {                                          newFilename = zipFilename ; }
608                }
609                else {
610                        newFilename = filename;
611                }
612
613                return new File( directory,newFilename );
614        }
615
616        /**
617         * replaceFrom,replaceTo に基づく禁則文字の置換処理を行います。
618         *
619         * replaceFrom の1文字づつを、対応するreplaceToの1文字づつに変換します。
620         * replaceFrom と replaceTo の文字数は同じでなければなりません。
621         *
622         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
623         *
624         * @param   str 置換する文字列
625         *
626         * @return      置換後の文字列
627         */
628        private String replace( final String str ) {
629                String rtn = str;
630                if( rtn != null && replaceFrom != null && replaceTo != null ) {
631                        for( int i=0; i<replaceTo.length(); i++ ) {
632                                rtn = rtn.replace( replaceFrom.charAt(i) , replaceTo.charAt(i) );               // charの置き換えは、全件
633                        }
634                }
635                return rtn ;
636        }
637
638        /**
639         * 引数配列付のクエリーを実行します。
640         * 処理自体は、#execute() と同様に、各サブクラスの実装に依存します。
641         * これは、CallableStatement を用いて、データベース検索処理を行います。
642         * {call TYPE3B01.TYPE3B01(?,?,?,?)} で、4番目の引数には、
643         * names で指定したリクエスト情報が、ARG_ARRAY 配列に順次セットされます。
644         * 使用する場合は、一旦わかり易い変数に受けて利用してください。
645         * 呼び出す PL/SQL では、検索系PL/SQL です。
646         *
647         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
648         * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応
649         * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し
650         * @og.rev 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
651         *
652         * @param       conn            コネクション
653         * @param   callStmt    コーラブルステートメント
654         * @param   args                オブジェクトの引数配列(可変長引数)
655         *
656         * @return      結果オブジェクト
657         */
658        private ResultSet executeCall( final Connection conn,final CallableStatement callStmt,final String... args ) throws SQLException {
659                callStmt.setQueryTimeout( DB_MAX_QUERY_TIMEOUT );
660                if( fetchSize > 0 ) { callStmt.setFetchSize( fetchSize ); }
661                final Map<String,Class<?>> map = conn.getTypeMap();
662                try {
663                        map.put( ERR_MSG,Class.forName( "org.opengion.hayabusa.db.DBErrMsg" ) );
664                }
665                catch( final ClassNotFoundException ex ) {
666                        final String errMsg = "org.opengion.hayabusa.db.DBErrMsg クラスが見つかりません。" + CR
667                                        + ex.getMessage();                      // // 5.1.8.0 (2010/07/01) errMsg 修正
668                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
669                }
670
671                // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応 http://docs.oracle.com/cd/E28389_01/web.1111/b60995/thirdparty.htm
672                final Array newArray = ((OracleConnection)conn).createOracleArray( ARG_ARRAY, StringUtil.rTrims( args ));               // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
673
674                callStmt.registerOutParameter(1, Types.INTEGER);
675                callStmt.registerOutParameter(2, Types.ARRAY,ERR_MSG_ARRAY);            // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
676                callStmt.registerOutParameter(3, OracleTypes.CURSOR);
677                callStmt.setArray( 4,newArray );                                                                        // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
678
679                callStmt.execute();
680
681                errCode = callStmt.getInt(1);
682
683                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
684                ResultSet resultSet = null;
685                if( errCode < ErrorMessage.NG ) {               // 異常以外の場合
686                        resultSet = ((OracleCallableStatement)callStmt).getCursor(3);
687                }
688                if( errCode > ErrorMessage.OK ) {               // 正常以外の場合
689                        final Array rtn3 = callStmt.getArray(2);                                                                // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
690                        final Object[] rtnval3 = (Object[])rtn3.getArray();
691                        errMessage = new ErrorMessage( "Query_JDBCErrMsg Error!!" );
692                        for( int i=0; i<rtnval3.length; i++ ) {
693                                final DBErrMsg er = (DBErrMsg)rtnval3[i];
694                                if( er == null ) { break; }
695                                errMessage.addMessage( er.getErrMsg() );
696                        }
697                }
698                return resultSet;
699        }
700
701        /**
702         * PrintWriter を取得します。
703         *
704         * ここでは、一般的なファイル出力を考慮した PrintWriter を作成します。
705         *
706         * @og.rev 3.7.1.1 (2005/05/23) フォルダがない場合は、複数階層分のフォルダを自動で作成します。
707         * @og.rev 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。
708         * @og.rev 5.6.1.0 (2013/02/01) 3.7.1.1のコメントに入っているが対応されていないのでフォルダ作成追加
709         * @og.rev 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
710         *
711         * @param        localFile 出力先Fileオブジェクト
712         * @return       出力用PrintWriterオブジェクト
713         * @og.rtnNotNull
714         */
715//      private PrintWriter getPrintWriter() {
716        private PrintWriter getPrintWriter( final File localFile ) {
717//              // 8.0.0.2 (2021/10/15) ディレクトリチェック、作成も考慮して makeLocalFile() で対応する。
718//              if( filename == null ) {
719//                      final String errMsg = "ファイル名がセットされていません。";
720//                      throw new HybsSystemException( errMsg );
721//              }
722//              final String directory = HybsSystem.url2dir( fileURL );
723
724//              // 5.6.1.0 (2013/02/01)
725//              final File dir = new File(directory);
726//              if( ! dir.exists() && ! dir.mkdirs() ) {
727//                      final String errMsg = "ディレクトリの作成に失敗しました。[" + directory + "]";
728//                      throw new HybsSystemException( errMsg );
729//              }
730
731                // ※ 注意 StringUtil.urlAppend を組み込んでいる意図が不明。一旦削除していますが、注意
732                // 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。
733        //      out = FileUtil.getPrintWriter( StringUtil.urlAppend( directory,filename ),fileAppend,encode);
734
735                // 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
736                // PrintWriterはローカルなので、appendする場合は、一旦、cloudからファイルを持ってくる必要がある。
737//              final File localFile = new File(directory, filename);
738                // 5.10.9.0 (2019/03/01) ADD クラウドストレージ利用、fileAppend、かつファイルが存在する場合はダウンロードする。
739                if( fileAppend && localFile.isFile() ) {
740                        HybsFileOperationFactory.cloud2local( useLocal,() -> localFile );
741                }
742//              final FileOperation cloudFile = HybsFileOperationFactory.create( directory, filename );
743//              if( cloudFile.isCloud() && fileAppend && cloudFile.isFile() ) {
744//                      localFile.delete();
745//                      FileUtil.copy( cloudFile, localFile );
746//              }
747
748                // 処理を簡素化します。
749//              return FileUtil.getPrintWriter( new File( directory,filename ),encode,fileAppend );
750                return FileUtil.getPrintWriter( localFile,encode,fileAppend );
751        }
752
753        /**
754         * 名称配列を元に、リクエスト情報のデータを取得します。
755         *
756         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
757         *
758         * @param       nameArray       キーとなる名称の配列(可変長引数)
759         *
760         * @return      そのリクエスト情報
761         * @og.rtnNotNull
762         */
763        private String[] getRequest( final String... nameArray ) {
764                String[] rtn = new String[nameArray.length];
765
766                for( int i=0; i<rtn.length; i++ ) {
767                        rtn[i] = getRequestValue( nameArray[i] );
768                }
769
770                return rtn;
771        }
772
773        /**
774         * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。
775         *
776         * @og.tag
777         *   検索時のDB接続IDを指定します。初期値は、DEFAULT です。
778         *
779         * @param       id DB接続ID
780         */
781        public void setDbid( final String id ) {
782                dbid = nval( getRequestParameter( id ),dbid );
783        }
784
785        /**
786         * 【TAG】可変長ファイルを作成するときの項目区切り文字(セパレータ)をセットします(初期値:TAB_SEPARATOR)。
787         *
788         * @og.tag 可変長ファイルを作成するときの項目区切り文字をセットします。
789         * (初期値:ローカル定義のTAB_SEPARATOR)。
790         *
791         * @param   sep セパレータ
792         * @see         #TAB_SEPARATOR
793         */
794        public void setSeparator( final String sep ) {
795                separator = nval( getRequestParameter( sep ),TAB_SEPARATOR );
796        }
797
798        /**
799         * 【TAG】保存先ディレクトリ名を指定します
800         *              (初期値:FILE_URL[={@og.value SystemData#FILE_URL}])。
801         *
802         * @og.tag
803         * この属性で指定されるディレクトリに、ファイルをセーブします。
804         * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、2文字目が、
805         * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
806         * fileURL = "{&#064;USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、
807         * さらに、各個人ID別のフォルダを作成して、そこにセーブします。
808         * (初期値:システム定数のFILE_URL[={@og.value SystemData#FILE_URL}])。
809         *
810         * @og.rev 3.5.4.3 (2004/01/05) 内部処理を、makeFileURL に移動。
811         * @og.rev 4.0.0.0 (2005/01/31) StringUtil.urlAppend メソッドの利用
812         * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。
813         * @og.rev 6.4.2.1 (2016/02/05) URLの最後に、"/" を追加する処理を廃止。
814         *
815         * @param       url 保存先ディレクトリ名
816         * @see         org.opengion.hayabusa.common.SystemData#FILE_URL
817         */
818        public void setFileURL( final String url ) {
819                final String furl = nval( getRequestParameter( url ),null );
820                if( furl != null ) {
821                        fileURL = StringUtil.urlAppend( fileURL,furl );
822                }
823        }
824
825        /**
826         * 【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME)。
827         *
828         * @og.tag ファイルを作成するときのファイル名をセットします。
829         *
830         * @param   fname ファイル名
831         */
832        public void setFilename( final String fname ) {
833                filename = nval( getRequestParameter( fname ),filename );
834        }
835
836        /**
837         * 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします
838         *              (初期値:FILE_ENCODE[={@og.value SystemData#FILE_ENCODE}])。
839         *
840         * @og.tag
841         * "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
842         * (初期値:システム定数のFILE_ENCODE[={@og.value SystemData#FILE_ENCODE}])。
843         *
844         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
845         * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。
846         *
847         * @param   enc ファイルエンコーディング名
848         * @see     <a href="http://www.iana.org/assignments/character-sets" target="_blank" >IANA Charset Registry</a>
849         * @see         org.opengion.hayabusa.common.SystemData#FILE_ENCODE
850         */
851        public void setEncode( final String enc ) {
852                encode = nval( getRequestParameter( enc ),encode );
853        }
854
855        /**
856         * 【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true)。
857         *
858         * @og.tag
859         *  #Name ・・・・ ヘッダーの書き込みを指定します。
860         * 通常は、書き込み(true)にしておき、使用側でコメントと解釈するように
861         * 処理を行うべきです。コメントのため、append モードで途中に現れても
862         * 無視できます。また、エンジン標準でデータを取り込む場合に、データの配置が
863         * 変更されても取り込みプログラムはそのまま使用できます。
864         * 初期値は、true(書き込む)です。
865         *
866         * @param   flag ヘッダー有無 [true:書き込む/false:書き込まない]
867         */
868        public void setUseHeader( final String flag ) {
869                useHeader = nval( getRequestParameter( flag ),useHeader );
870        }
871
872        /**
873         * 【TAG】データをダブルクオートで囲うかどうか指定します(初期値:false)。
874         *
875         * @og.tag
876         * データを出力する場合、ダブルクオートで囲うかどうか指定します。
877         * 主に、区切り文字(separator)を、タブではなく、カンマを使う場合に、使用します。
878         * なお、ヘッダー部は、この指定に関わらず、ダブルクオートで囲いません。
879         * 初期値は、false(囲わない)です。
880         *
881         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
882         *
883         * @param   flag ダブルクオート使用 [true:書き込む/false:書き込まない]
884         */
885        public void setUseQuote( final String flag ) {
886                useQuote = nval( getRequestParameter( flag ),useQuote );
887        }
888
889        /**
890         * 【TAG】データ中にダブルクオート文字が含まれる場合、エスケープするかどうか指定します(初期値:true)。
891         *
892         * @og.tag
893         * データ中にダブルクオート文字が含まれる場合、エスケープするかどうか指定します。
894         * ここでいうエスケープとは、ダブルクオート文字を重ねる処理を指します。
895         * 初期値は、互換性の関係で、true(処理する)です。
896         *
897         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
898         *
899         * @param   flag ダブルクオートエスケープ有無 [true:する/false:しない]
900         */
901        public void setUseQuoteEscape( final String flag ) {
902                useQuoteEscape = nval( getRequestParameter( flag ),useQuoteEscape );
903        }
904
905        /**
906         * 【TAG】データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか指定します(初期値:true)。
907         *
908         * @og.tag
909         * データ中に改行コードが含まれたテキストの場合、EXCELで開くと、改行されてしまう。
910         * その場合、ダブルクオートで囲うと、セルに設定してくれます。
911         * この処理は、useQuote="true" にすると、無条件に、データは囲われます。
912         * 初期値は、互換性の関係で、true(処理する)です。
913         *
914         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
915         *
916         * @param   flag 改行コード処理 [true:する/false:しない]
917         * @see         #setUseQuote( String )
918         */
919        public void setUseReturnQuote( final String flag ) {
920                useReturnQuote = nval( getRequestParameter( flag ),useReturnQuote );
921        }
922
923        /**
924         * 【TAG】一文字単位で置換する置換元文字を指定します(初期値:null 置換なし)。
925         *
926         * @og.tag
927         * データ出力時に、禁則文字を、置き換える元の文字を指定します。
928         * ここでは、一文字単位で、置換しますので、禁則文字は、連続の文字列の
929         * 形で、指定します。
930         * なお、ヘッダー部は、この指定に関わらず、ダブルクオートで囲いません。
931         * 初期値は、null の場合は、何も変換しません。
932         * 文字数は、replaceTo と同じでなければなりません。
933         *
934         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
935         * @og.rev 6.2.2.0 (2015/03/27) \n,\r,\t をサポートします。
936         *
937         * @param   str 置換元文字
938         */
939        public void setReplaceFrom( final String str ) {
940                replaceFrom = changeRNT( nval( getRequestParameter( str ),replaceFrom ) );
941        }
942
943        /**
944         * 【TAG】一文字単位で置換する置換先文字を指定します。
945         *
946         * @og.tag
947         * データ出力時に、禁則文字を、置き換える先の文字を指定します。
948         * ここでは、一文字単位で、置換しますので、禁則文字は、連続の文字列の
949         * 形で、指定します。(例えば、全角文字にするとか)
950         * 初期値は、null の場合は、何も変換しません。
951         * 文字数は、replaceFrom と同じでなければなりません。
952         *
953         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
954         * @og.rev 6.2.2.0 (2015/03/27) \n,\r,\t をサポートします。
955         *
956         * @param   str 置換先文字
957         */
958        public void setReplaceTo( final String str ) {
959                replaceTo = changeRNT( nval( getRequestParameter( str ),replaceTo ) );
960        }
961
962        /**
963         * replaceFrom,replaceTo で、\n,\r,\t をサポートします。
964         *
965         * データ置換で、改行、復帰、タブを、指定する場合、2文字必要です。
966         * ここでは、\n,\r,\t が指定された場合、キャラクタコードに置き換えます。
967         *
968         * @og.rev 6.2.2.0 (2015/03/27) \n,\r,\t をサポートします。
969         *
970         * @param       str 置換先文字
971         * @return      置換先文字
972         */
973        private String changeRNT( final String str ) {
974                String rtn = str ;
975                if( rtn != null && !rtn.isEmpty() ) {
976                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
977                        for( int i=0; i<rtn.length(); i++ ) {
978                                char ch = rtn.charAt(i) ;
979                                if( ch == '\\' ) {
980                                        final char ch2 = rtn.charAt(++i) ;              // ¥ の次の文字(n,r,tのみサポート)
981                                        switch( ch2 ) {
982                                                case 'n' : ch = '\n'; break;
983                                                case 'r' : ch = '\r'; break;
984                                                case 't' : ch = '\t'; break;
985                                                default  :
986                                                        final String errMsg = getClass().getName()
987                                                                                         + "の置換文字列で、「\\" + ch2 + "」は、サポートされていません。";
988                                                        System.err.println( errMsg );
989                                                        break;
990                                        }
991                                }
992                                buf.append( ch );
993                        }
994                        rtn = buf.toString();
995                }
996                return rtn ;
997        }
998
999        /**
1000         * 【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[新規モード])。
1001         *
1002         * @og.tag
1003         * ファイルを書き込む場合、追加モードで書き込むかどうかをセットします。
1004         * 新規モード(true)の場合、既存のファイルが存在し、かつ書き込み許可があれば、
1005         * 上書きで新規に作成します。
1006         * 初期値は、false(新規モード)です。
1007         *
1008         * @param   flag 追加モード [true:追加モード/false:新規モード]
1009         */
1010        public void setFileAppend( final String flag ) {
1011                fileAppend = nval( getRequestParameter( flag ),fileAppend );
1012        }
1013
1014        /**
1015         * 【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false)。
1016         *
1017         * @og.tag
1018         * 大量に抜き出す場合、そのまま、サーバーから取り出すだけでも大変です。
1019         * zip 属性を、true にすると、GZIP で圧縮したファイルを作成します。
1020         * 初期値は、false(圧縮しない)です。
1021         *
1022         * @param  flag ZIP圧縮 [true:する/それ以外:しない]
1023         * @see    #setZipFilename( String )
1024         */
1025        public void setZip( final String flag ) {
1026                zip = nval( getRequestParameter( flag ),zip );
1027        }
1028
1029        /**
1030         * 【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip")。
1031         *
1032         * @og.tag
1033         * zip 属性に、true を指定した場合に、ZIPファイル化します。その場合のファイル名を指定します。
1034         * なにも指定しない場合は、filename + ".zip" になります。
1035         *
1036         * @param   zipFile ZIPファイル名
1037         * @see #setZip( String )
1038         */
1039        public void setZipFilename( final String zipFile ) {
1040                zipFilename = nval( getRequestParameter( zipFile ),zipFilename );
1041        }
1042
1043        /**
1044         * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します
1045         *              (初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
1046         *
1047         * @og.tag
1048         * ここでは、検索結果の件数や登録された件数をまず出力し、
1049         * その次に、ここで指定したメッセージをリソースから取得して
1050         * 表示します。
1051         * 件数を表示させる場合は、displayMsg = "MSG0033"[ 件検索しました] をセットしてください。
1052         * 表示させたくない場合は、displayMsg = "" をセットしてください。
1053         * (初期値:システム定数のVIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
1054         *
1055         * @param       id 結果表示メッセージID
1056         */
1057        public void setDisplayMsg( final String id ) {
1058                final String ids = getRequestParameter( id );
1059                if( ids != null ) { displayMsg = ids; }
1060        }
1061
1062        /**
1063         * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。
1064         *
1065         * @og.tag
1066         * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。
1067         * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、
1068         * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。
1069         * 表示させたくない場合は、notfoundMsg = "" をセットしてください。
1070         * 初期値は、MSG0077[対象データはありませんでした]です。
1071         *
1072         * @param       id ゼロ件時表示メッセージID
1073         */
1074        public void setNotfoundMsg( final String id ) {
1075                final String ids = getRequestParameter( id );
1076                if( ids != null ) { notfoundMsg = ids; }
1077        }
1078
1079        /**
1080         * 【TAG】(通常は使いません)データのフェッチサイズを指定します
1081         *              (初期値:DB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。
1082         *
1083         * @og.tag
1084         * より多くの行が必要なときに、データベースから取り出す必要がある行数に
1085         * ついてのヒントを JDBC ドライバに提供します。
1086         * 指定された行数は、この Statement を使って作成された結果セットにだけ影響します。
1087         * 指定された値が 0 の場合、ヒントは無視されます。
1088         * (初期値:システム定数のDB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。
1089         *
1090         * @param       size フェッチ行数
1091         */
1092        public void setFetchSize( final String size ) {
1093                fetchSize = nval( getRequestParameter( size ),fetchSize );
1094        }
1095
1096        /**
1097         * 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します。
1098         *
1099         * @og.tag
1100         * 複数ある場合は、CSV形式で渡します。
1101         * PL/SQL を使用しない場合は、無視されます。
1102         *
1103         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
1104         *
1105         * @param       nm 引数の名称 (CSV形式)
1106         */
1107        public void setNames( final String nm ) {
1108                names = nval( getRequestParameter( nm ),names );
1109        }
1110
1111        /**
1112         * 【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC})。
1113         *
1114         * @og.tag
1115         * ストアドプロシージャ等を実行する場合に、queryType="JDBCErrMsg" を
1116         * 指定する必要があります。(それ以外の指定は、初期値の JDBC になります。)
1117         * 初期値は、"JDBC" です。
1118         * {@og.doc03Link queryType Query_**** クラス}
1119         *
1120         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
1121         *
1122         * @param       id Query発行クラスID
1123         */
1124        public void setQueryType( final String id ) {
1125                // 内部的には、JDBCErrMsg:false / それ以外:true で管理しています。
1126                queryType = ! "JDBCErrMsg".equalsIgnoreCase( getRequestParameter( id ) );
1127        }
1128
1129        /**
1130         * 【TAG】ファイル出力時に、行番号情報を、出力する/しない[true/false]を指定します(初期値:true)。
1131         *
1132         * @og.tag
1133         * 通常のフォーマットでは、各行の先頭に行番号を出力します。
1134         * これは、#NAME 属性を使用する場合には、必ず出力する必要があります。
1135         * (#NAME 属性は、読み取り時にあれば、自動的にカラムに割り当てられます。)
1136         * この、先頭の行番号が不要な場合(つまり、他のシステムへのデータ出力等)
1137         * の為に出力する場合に、false を設定することで、行番号列を出力しない
1138         * ようにできます。
1139         * 初期値は、true(出力する) です。
1140         *
1141         * @og.rev 5.5.7.1 (2012/10/05) 新規追加
1142         * @param  flag 行番号出力 [true:する/それ以外:しない]
1143         */
1144        public void setUseNumber( final String flag ) {
1145                useNumber = nval( getRequestParameter( flag ),useNumber );
1146        }
1147
1148        /**
1149         * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します
1150         *              (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。
1151         *
1152         * @og.tag
1153         * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに
1154         * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。
1155         * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、
1156         * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、
1157         * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。
1158         * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
1159         * 初期値は、SystemData#USE_SQL_INJECTION_CHECK です。
1160         *
1161         * @og.rev 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
1162         *
1163         * @param   flag クォートチェック [true:する/それ以外:しない]
1164         */
1165        public void setQuotCheck( final String flag ) {
1166                quotCheck = nval( getRequestParameter( flag ),quotCheck );
1167        }
1168
1169        /**
1170         * 【TAG】リクエスト情報の HTMLTag開始/終了文字(&gt;&lt;) 存在チェックを実施するかどうか[true/false]を設定します
1171         *              (初期値:USE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。
1172         *
1173         * @og.tag
1174         * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
1175         * (&gt;&lt;) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
1176         * (初期値:システム定数のUSE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])
1177         *
1178         * @og.rev 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
1179         *
1180         * @param       flag    XSSチェック [true:する/false:しない]
1181         * @see         org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
1182         */
1183        public void setXssCheck( final String flag ) {
1184                xssCheck = nval( getRequestParameter( flag ),xssCheck );
1185        }
1186
1187        /**
1188         * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します
1189         *              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
1190         *
1191         * @og.tag
1192         * true に設定すると、処理時間を表示するバーイメージが表示されます。
1193         * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで
1194         * 表示させる機能です。処理時間の目安になります。
1195         * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
1196         *
1197         * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。
1198         *
1199         * @param       flag    処理時間を表示 [true:する/false:しない]
1200         */
1201        public void setUseTimeView( final String flag ) {
1202                useTimeView = nval( getRequestParameter( flag ),useTimeView );
1203        }
1204
1205        /**
1206         * 【TAG】エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
1207         *
1208         * @og.tag
1209         * 通常のエラーメッセージは、ラベル(長)が使われますが、これをラベル(短)を使いたい場合に、true にセットします。
1210         * ここでのラベル(短)は、タグ修飾なしの、ラベル(短)です。
1211         * 標準はfalse:利用しない=ラベル(長)です。
1212         * true/false以外を指定した場合はfalse扱いとします。
1213         *
1214         * ラベルリソースの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
1215         *
1216         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
1217         *
1218         * @param prm SLABEL利用 [true:利用する/false:利用しない]
1219         */
1220        public void setUseSLabel( final String prm ) {
1221                useSLabel = nval( getRequestParameter( prm ),useSLabel );
1222        }
1223
1224        /**
1225         * 【TAG】システム定数でクラウド設定されていても、クラウド環境を使用しない場合、trueを指定します(初期値:false)。
1226         *
1227         * @og.tag
1228         * クラウド設定は、システム定数の『CLOUD_TARGET』と『CLOUD_BUCKET』の設定で自動的に使用しますが、
1229         * どうしてもローカルでのみ使いたい場合は、この属性を true に設定します。
1230         * 標準はfalse:設定どおりとなります。
1231         *
1232         * true/false以外を指定した場合はfalse扱いとします。
1233         *
1234         * @og.rev 8.0.1.0 (2021/10/29) useLocal 属性を追加。
1235         *
1236         * @param flag ローカル環境のみ [true:ローカルのみ/false:設定どおり]
1237         */
1238        public void setUseLocal( final String flag ) {
1239                useLocal = nval( getRequestParameter( flag ),useLocal );
1240        }
1241
1242        /**
1243         * 【TAG】valueタグのaction=MAPOBJ を使用したラベル変換を行う場合の、MAPOBJキーを指定します。
1244         *
1245         * @og.tag
1246         *
1247         * DirectWriteTableTagでは、SQL文から直接ファイルを作成するため、headLabel はresource から
1248         * 取得します。
1249         * 通常の DBTableModel を作成する場合には、ColumnEditorTag でuseLabelMapとしてSQL文でキーとラベルの
1250         * Mapを作成してラベルの書き換えが行えますが、それと同等の事を行うためには、一旦、valueタグで、
1251         * command="SQL" action=MAPOBJ で、キーとラベルのMapを作成して、ラベルとして使用します。
1252         * なお、Mapオブジェクトは、scope="request" でのみやり取り可能です。
1253         *
1254         * @og.rev 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
1255         *
1256         * @param key valueタグで作成したMAPOBJを取り出すときのキー
1257         */
1258        public void setMapObjKey( final String key ) {
1259                mapObjKey = nval( getRequestParameter( key ),mapObjKey );
1260        }
1261
1262        /**
1263         * このオブジェクトの文字列表現を返します。
1264         * 基本的にデバッグ目的に使用します。
1265         *
1266         * @return このクラスの文字列表現
1267         * @og.rtnNotNull
1268         */
1269        @Override
1270        public String toString() {
1271                return ToString.title( this.getClass().getName() )
1272                                .println( "VERSION"             ,VERSION        )
1273                                .println( "dbid"                ,dbid           )
1274                                .println( "separator"   ,separator      )
1275                                .println( "useHeader"   ,useHeader      )
1276                                .println( "fileURL"             ,fileURL        )
1277                                .println( "filename"    ,filename       )
1278                                .println( "sql"                 ,sql            )
1279                                .println( "encode"              ,encode         )
1280                                .println( "fileAppend"  ,fileAppend     )
1281                                .println( "zip"                 ,zip            )
1282                                .println( "zipFilename" ,zipFilename)
1283                                .println( "displayMsg"  ,displayMsg     )
1284                                .println( "fetchSize"   ,fetchSize      )
1285                                .println( "queryType"   ,queryType      )
1286                                .println( "names"               ,names          )
1287                                .println( "errCode"             ,errCode        )
1288                                .println( "Other..."    ,getAttributes().getAttribute() )
1289                                .fixForm().toString() ;
1290        }
1291}