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
506                                        mapObj = (Map<String,String>)getRequestAttribute( mapObjKey );
507                                }
508
509                                final ResourceManager resource = getResource();
510                                // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
511                                for( int clmNo=0; clmNo<numberOfColumns; clmNo++ ) {
512                                        final String clm = rsv.getColumnName(clmNo);
513                                        if( clmNo > 0 || useNumber ){                                           // 5.5.7.1 (2012/10/05)
514                                                //この場合だけセパレータ出力する。
515                                                headName.append( separator );                                   // 6.0.3.0 (2014/11/13)
516                                                headLabel.append( separator );                                  // 6.0.3.0 (2014/11/13)
517                                        }
518                                        headName.append( clm );                                                         // 6.0.3.0 (2014/11/13)
519//                                      headLabel.append( resource.getLabel( clm ) );           // 6.0.3.0 (2014/11/13)
520                                        // 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
521                                        String lbl = resource.getLabel( clm );
522                                        if( mapObj != null ) {
523                                                lbl = mapObj.getOrDefault( clm,lbl );                   // 7.0.7.1 (2019/12/24) Mapの値を優先して、null なら リソース使用
524                                        }
525                                        headLabel.append( lbl );                                                        // 6.0.3.0 (2014/11/13)
526                                }
527                                outPW.println( headName.toString() );
528                                outPW.println( headLabel.toString() );
529                        }
530
531                        int rowNo = 0;
532                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
533                        while( rsv.next() ) {
534                                // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
535                                if( useNumber ){                                        // 5.5.7.1 (2012/10/05)
536                                        if( useQuote ) { outPW.print( "\"" + rowNo + "\"" ); }  // 行番号
537                                        else {                   outPW.print( rowNo ); }
538                                }
539                                for( int clmNo=0; clmNo<numberOfColumns; clmNo++ ) {
540                                        // 6.0.2.5 (2014/10/31) refactoring:Avoid empty if statements 警告の対応
541                                        if( clmNo > 0 || useNumber ){                                   // 6.2.0.0 (2015/02/27)
542                                                //この場合だけセパレータ出力する。
543                                                outPW.print( separator );
544                                        }
545                                        String sval = replace( rsv.getValue(clmNo) );                           // 禁則文字の置換処理
546                                        if( sval != null && sval.length() > 0 ) {
547                                                // 6.0.3.0 (2014/11/13) データ中にダブルクオート文字が含まれる場合、エスケープするかどうか
548                                                if( useQuoteEscape && sval.indexOf( '"' ) >= 0 ) { sval = sval.replaceAll( "\"" ,"\"\"" ) ; }
549                                                // 6.0.3.0 (2014/11/13) データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか
550                                                if( useQuote || useReturnQuote && sval.indexOf( CR ) >= 0 ) {
551                                                        sval = "\"" + sval + "\"" ;
552                                                }
553                                        }
554                                        else {
555                                                sval = useQuote ? "\"\"" : "" ;
556                                        }
557                                        outPW.print( sval );
558                                }
559                                outPW.println();
560                                rowNo++ ;
561                        }
562                        executeCount = rowNo ;
563                        tran.commit();                          // 6.3.6.1 (2015/08/28)
564                }
565                catch( final SQLException ex ) {                // catch は、close() されてから呼ばれます。
566                        final String errMsg = "データベース処理を実行できませんでした。"
567                                                 + CR + '[' + sql + ']' + CR
568                                                 + "err=[" + ex.getSQLState() + ']'
569                                                 + ex.getMessage();
570                        throw new HybsSystemException( errMsg,ex );
571                }
572                finally {                                               // finally は、close() されてから呼ばれます。
573                        Closer.resultClose( resultSet );
574                        Closer.stmtClose( stmt );
575                        Closer.stmtClose( callStmt );   // 4.3.4.3 (2008/12/22)
576                }
577
578                return executeCount ;
579        }
580
581        /**
582         * ローカルファイルを作成します。
583         *
584         * ディレクトリの存在チェックとなければ作成します。
585         * zipファイルの指定がされて、zipFilenameが指定されていない場合は、filename + ".zip" とします。
586         *
587         * @og.rev 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
588         *
589         * @return      ローカルファイルオブジェクト
590         */
591        private File makeLocalFile() {
592                if( filename == null ) {
593                        final String errMsg = "ファイル名がセットされていません。";
594                        throw new HybsSystemException( errMsg );
595                }
596                final String directory = HybsSystem.url2dir( fileURL );                 // こちらは、ローカル変数でよい。
597
598                // 5.6.1.0 (2013/02/01)
599                final File dir = new File(directory);
600                if( ! dir.exists() && ! dir.mkdirs() ) {
601                        final String errMsg = "ディレクトリの作成に失敗しました。[" + directory + "]";
602                        throw new HybsSystemException( errMsg );
603                }
604
605                final String newFilename ;
606                if( zip ) {
607                        if( zipFilename == null ) { newFilename = filename + ".zip"; }
608                        else {                                          newFilename = zipFilename ; }
609                }
610                else {
611                        newFilename = filename;
612                }
613
614                return new File( directory,newFilename );
615        }
616
617        /**
618         * replaceFrom,replaceTo に基づく禁則文字の置換処理を行います。
619         *
620         * replaceFrom の1文字づつを、対応するreplaceToの1文字づつに変換します。
621         * replaceFrom と replaceTo の文字数は同じでなければなりません。
622         *
623         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
624         *
625         * @param   str 置換する文字列
626         *
627         * @return      置換後の文字列
628         */
629        private String replace( final String str ) {
630                String rtn = str;
631                if( rtn != null && replaceFrom != null && replaceTo != null ) {
632                        for( int i=0; i<replaceTo.length(); i++ ) {
633                                rtn = rtn.replace( replaceFrom.charAt(i) , replaceTo.charAt(i) );               // charの置き換えは、全件
634                        }
635                }
636                return rtn ;
637        }
638
639        /**
640         * 引数配列付のクエリーを実行します。
641         * 処理自体は, #execute() と同様に、各サブクラスの実装に依存します。
642         * これは、CallableStatement を用いて、データベース検索処理を行います。
643         * {call TYPE3B01.TYPE3B01(?,?,?,?)} で、4番目の引数には、
644         * names で指定したリクエスト情報が、ARG_ARRAY 配列に順次セットされます。
645         * 使用する場合は、一旦わかり易い変数に受けて利用してください。
646         * 呼び出す PL/SQL では、検索系PL/SQL です。
647         *
648         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
649         * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応
650         * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し
651         * @og.rev 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
652         *
653         * @param       conn            コネクション
654         * @param   callStmt    コーラブルステートメント
655         * @param   args                オブジェクトの引数配列(可変長引数)
656         *
657         * @return      結果オブジェクト
658         */
659        private ResultSet executeCall( final Connection conn,final CallableStatement callStmt,final String... args ) throws SQLException {
660                callStmt.setQueryTimeout( DB_MAX_QUERY_TIMEOUT );
661                if( fetchSize > 0 ) { callStmt.setFetchSize( fetchSize ); }
662                final Map<String,Class<?>> map = conn.getTypeMap();
663                try {
664                        map.put( ERR_MSG,Class.forName( "org.opengion.hayabusa.db.DBErrMsg" ) );
665                }
666                catch( final ClassNotFoundException ex ) {
667                        final String errMsg = "org.opengion.hayabusa.db.DBErrMsg クラスが見つかりません。" + CR
668                                        + ex.getMessage();                      // // 5.1.8.0 (2010/07/01) errMsg 修正
669                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
670                }
671
672                // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応 http://docs.oracle.com/cd/E28389_01/web.1111/b60995/thirdparty.htm
673                final Array newArray = ((OracleConnection)conn).createOracleArray( ARG_ARRAY, StringUtil.rTrims( args ));               // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
674
675                callStmt.registerOutParameter(1, Types.INTEGER);
676                callStmt.registerOutParameter(2, Types.ARRAY,ERR_MSG_ARRAY);            // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
677                callStmt.registerOutParameter(3, OracleTypes.CURSOR);
678                callStmt.setArray( 4,newArray );                                                                        // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
679
680                callStmt.execute();
681
682                errCode = callStmt.getInt(1);
683
684                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
685                ResultSet resultSet = null;
686                if( errCode < ErrorMessage.NG ) {               // 異常以外の場合
687                        resultSet = ((OracleCallableStatement)callStmt).getCursor(3);
688                }
689                if( errCode > ErrorMessage.OK ) {               // 正常以外の場合
690                        final Array rtn3 = callStmt.getArray(2);                                                                // 6.0.0.0 (2014/04/11) Oracle11g(11.2.0.3のドライバ)対応
691                        final Object[] rtnval3 = (Object[])rtn3.getArray();
692                        errMessage = new ErrorMessage( "Query_JDBCErrMsg Error!!" );
693                        for( int i=0; i<rtnval3.length; i++ ) {
694                                final DBErrMsg er = (DBErrMsg)rtnval3[i];
695                                if( er == null ) { break; }
696                                errMessage.addMessage( er.getErrMsg() );
697                        }
698                }
699                return resultSet;
700        }
701
702        /**
703         * PrintWriter を取得します。
704         *
705         * ここでは、一般的なファイル出力を考慮した PrintWriter を作成します。
706         *
707         * @og.rev 3.7.1.1 (2005/05/23) フォルダがない場合は、複数階層分のフォルダを自動で作成します。
708         * @og.rev 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。
709         * @og.rev 5.6.1.0 (2013/02/01) 3.7.1.1のコメントに入っているが対応されていないのでフォルダ作成追加
710         * @og.rev 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
711         *
712         * @param        localFile 出力先Fileオブジェクト
713         * @return       出力用PrintWriterオブジェクト
714         * @og.rtnNotNull
715         */
716//      private PrintWriter getPrintWriter() {
717        private PrintWriter getPrintWriter( final File localFile ) {
718//              // 8.0.0.2 (2021/10/15) ディレクトリチェック、作成も考慮して makeLocalFile() で対応する。
719//              if( filename == null ) {
720//                      final String errMsg = "ファイル名がセットされていません。";
721//                      throw new HybsSystemException( errMsg );
722//              }
723//              final String directory = HybsSystem.url2dir( fileURL );
724
725//              // 5.6.1.0 (2013/02/01)
726//              final File dir = new File(directory);
727//              if( ! dir.exists() && ! dir.mkdirs() ) {
728//                      final String errMsg = "ディレクトリの作成に失敗しました。[" + directory + "]";
729//                      throw new HybsSystemException( errMsg );
730//              }
731
732                // ※ 注意 StringUtil.urlAppend を組み込んでいる意図が不明。一旦削除していますが、注意
733                // 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。
734        //      out = FileUtil.getPrintWriter( StringUtil.urlAppend( directory,filename ),fileAppend,encode);
735
736                // 8.0.0.2 (2021/10/15) ローカルファイルとクラウドファイル間の移動
737                // PrintWriterはローカルなので、appendする場合は、一旦、cloudからファイルを持ってくる必要がある。
738//              final File localFile = new File(directory, filename);
739                // 5.10.9.0 (2019/03/01) ADD クラウドストレージ利用、fileAppend、かつファイルが存在する場合はダウンロードする。
740                if( fileAppend && localFile.isFile() ) {
741                        HybsFileOperationFactory.cloud2local( useLocal,() -> localFile );
742                }
743//              final FileOperation cloudFile = HybsFileOperationFactory.create( directory, filename );
744//              if( cloudFile.isCloud() && fileAppend && cloudFile.isFile() ) {
745//                      localFile.delete();
746//                      FileUtil.copy( cloudFile, localFile );
747//              }
748
749                // 処理を簡素化します。
750//              return FileUtil.getPrintWriter( new File( directory,filename ),encode,fileAppend );
751                return FileUtil.getPrintWriter( localFile,encode,fileAppend );
752        }
753
754        /**
755         * 名称配列を元に、リクエスト情報のデータを取得します。
756         *
757         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
758         *
759         * @param       nameArray       キーとなる名称の配列(可変長引数)
760         *
761         * @return      そのリクエスト情報
762         * @og.rtnNotNull
763         */
764        private String[] getRequest( final String... nameArray ) {
765                String[] rtn = new String[nameArray.length];
766
767                for( int i=0; i<rtn.length; i++ ) {
768                        rtn[i] = getRequestValue( nameArray[i] );
769                }
770
771                return rtn;
772        }
773
774        /**
775         * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。
776         *
777         * @og.tag
778         *   検索時のDB接続IDを指定します。初期値は、DEFAULT です。
779         *
780         * @param       id DB接続ID
781         */
782        public void setDbid( final String id ) {
783                dbid = nval( getRequestParameter( id ),dbid );
784        }
785
786        /**
787         * 【TAG】可変長ファイルを作成するときの項目区切り文字(セパレータ)をセットします(初期値:TAB_SEPARATOR)。
788         *
789         * @og.tag 可変長ファイルを作成するときの項目区切り文字をセットします。
790         * (初期値:ローカル定義のTAB_SEPARATOR)。
791         *
792         * @param   sep セパレータ
793         * @see         #TAB_SEPARATOR
794         */
795        public void setSeparator( final String sep ) {
796                separator = nval( getRequestParameter( sep ),TAB_SEPARATOR );
797        }
798
799        /**
800         * 【TAG】保存先ディレクトリ名を指定します
801         *              (初期値:FILE_URL[={@og.value SystemData#FILE_URL}])。
802         *
803         * @og.tag
804         * この属性で指定されるディレクトリに、ファイルをセーブします。
805         * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、2文字目が、
806         * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
807         * fileURL = "{&#064;USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、
808         * さらに、各個人ID別のフォルダを作成して、そこにセーブします。
809         * (初期値:システム定数のFILE_URL[={@og.value SystemData#FILE_URL}])。
810         *
811         * @og.rev 3.5.4.3 (2004/01/05) 内部処理を、makeFileURL に移動。
812         * @og.rev 4.0.0.0 (2005/01/31) StringUtil.urlAppend メソッドの利用
813         * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。
814         * @og.rev 6.4.2.1 (2016/02/05) URLの最後に、"/" を追加する処理を廃止。
815         *
816         * @param       url 保存先ディレクトリ名
817         * @see         org.opengion.hayabusa.common.SystemData#FILE_URL
818         */
819        public void setFileURL( final String url ) {
820                final String furl = nval( getRequestParameter( url ),null );
821                if( furl != null ) {
822                        fileURL = StringUtil.urlAppend( fileURL,furl );
823                }
824        }
825
826        /**
827         * 【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME)。
828         *
829         * @og.tag ファイルを作成するときのファイル名をセットします。
830         *
831         * @param   fname ファイル名
832         */
833        public void setFilename( final String fname ) {
834                filename = nval( getRequestParameter( fname ),filename );
835        }
836
837        /**
838         * 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします
839         *              (初期値:FILE_ENCODE[={@og.value SystemData#FILE_ENCODE}])。
840         *
841         * @og.tag
842         * "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
843         * (初期値:システム定数のFILE_ENCODE[={@og.value SystemData#FILE_ENCODE}])。
844         *
845         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
846         * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。
847         *
848         * @param   enc ファイルエンコーディング名
849         * @see     <a href="http://www.iana.org/assignments/character-sets">IANA Charset Registry</a>
850         * @see         org.opengion.hayabusa.common.SystemData#FILE_ENCODE
851         */
852        public void setEncode( final String enc ) {
853                encode = nval( getRequestParameter( enc ),encode );
854        }
855
856        /**
857         * 【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true)。
858         *
859         * @og.tag
860         *  #Name ・・・・ ヘッダーの書き込みを指定します。
861         * 通常は、書き込み(true)にしておき、使用側でコメントと解釈するように
862         * 処理を行うべきです。コメントのため、append モードで途中に現れても
863         * 無視できます。また、エンジン標準でデータを取り込む場合に、データの配置が
864         * 変更されても取り込みプログラムはそのまま使用できます。
865         * 初期値は、true(書き込む)です。
866         *
867         * @param   flag ヘッダー有無 [true:書き込む/false:書き込まない]
868         */
869        public void setUseHeader( final String flag ) {
870                useHeader = nval( getRequestParameter( flag ),useHeader );
871        }
872
873        /**
874         * 【TAG】データをダブルクオートで囲うかどうか指定します(初期値:false)。
875         *
876         * @og.tag
877         * データを出力する場合、ダブルクオートで囲うかどうか指定します。
878         * 主に、区切り文字(separator)を、タブではなく、カンマを使う場合に、使用します。
879         * なお、ヘッダー部は、この指定に関わらず、ダブルクオートで囲いません。
880         * 初期値は、false(囲わない)です。
881         *
882         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
883         *
884         * @param   flag ダブルクオート使用 [true:書き込む/false:書き込まない]
885         */
886        public void setUseQuote( final String flag ) {
887                useQuote = nval( getRequestParameter( flag ),useQuote );
888        }
889
890        /**
891         * 【TAG】データ中にダブルクオート文字が含まれる場合、エスケープするかどうか指定します(初期値:true)。
892         *
893         * @og.tag
894         * データ中にダブルクオート文字が含まれる場合、エスケープするかどうか指定します。
895         * ここでいうエスケープとは、ダブルクオート文字を重ねる処理を指します。
896         * 初期値は、互換性の関係で、true(処理する)です。
897         *
898         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
899         *
900         * @param   flag ダブルクオートエスケープ有無 [true:する/false:しない]
901         */
902        public void setUseQuoteEscape( final String flag ) {
903                useQuoteEscape = nval( getRequestParameter( flag ),useQuoteEscape );
904        }
905
906        /**
907         * 【TAG】データ中に改行コードが含まれる場合、ダブルクオートで囲うかどうか指定します(初期値:true)。
908         *
909         * @og.tag
910         * データ中に改行コードが含まれたテキストの場合、EXCELで開くと、改行されてしまう。
911         * その場合、ダブルクオートで囲うと、セルに設定してくれます。
912         * この処理は、useQuote="true" にすると、無条件に、データは囲われます。
913         * 初期値は、互換性の関係で、true(処理する)です。
914         *
915         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
916         *
917         * @param   flag 改行コード処理 [true:する/false:しない]
918         * @see         #setUseQuote( String )
919         */
920        public void setUseReturnQuote( final String flag ) {
921                useReturnQuote = nval( getRequestParameter( flag ),useReturnQuote );
922        }
923
924        /**
925         * 【TAG】一文字単位で置換する置換元文字を指定します(初期値:null 置換なし)。
926         *
927         * @og.tag
928         * データ出力時に、禁則文字を、置き換える元の文字を指定します。
929         * ここでは、一文字単位で、置換しますので、禁則文字は、連続の文字列の
930         * 形で、指定します。
931         * なお、ヘッダー部は、この指定に関わらず、ダブルクオートで囲いません。
932         * 初期値は、null の場合は、何も変換しません。
933         * 文字数は、replaceTo と同じでなければなりません。
934         *
935         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
936         * @og.rev 6.2.2.0 (2015/03/27) \n,\r,\t をサポートします。
937         *
938         * @param   str 置換元文字
939         */
940        public void setReplaceFrom( final String str ) {
941                replaceFrom = changeRNT( nval( getRequestParameter( str ),replaceFrom ) );
942        }
943
944        /**
945         * 【TAG】一文字単位で置換する置換先文字を指定します。
946         *
947         * @og.tag
948         * データ出力時に、禁則文字を、置き換える先の文字を指定します。
949         * ここでは、一文字単位で、置換しますので、禁則文字は、連続の文字列の
950         * 形で、指定します。(例えば、全角文字にするとか)
951         * 初期値は、null の場合は、何も変換しません。
952         * 文字数は、replaceFrom と同じでなければなりません。
953         *
954         * @og.rev 6.0.3.0 (2014/11/13) 新規追加
955         * @og.rev 6.2.2.0 (2015/03/27) \n,\r,\t をサポートします。
956         *
957         * @param   str 置換先文字
958         */
959        public void setReplaceTo( final String str ) {
960                replaceTo = changeRNT( nval( getRequestParameter( str ),replaceTo ) );
961        }
962
963        /**
964         * replaceFrom,replaceTo で、\n,\r,\t をサポートします。
965         *
966         * データ置換で、改行、復帰、タブを、指定する場合、2文字必要です。
967         * ここでは、\n,\r,\t が指定された場合、キャラクタコードに置き換えます。
968         *
969         * @og.rev 6.2.2.0 (2015/03/27) \n,\r,\t をサポートします。
970         *
971         * @param       str 置換先文字
972         * @return      置換先文字
973         */
974        private String changeRNT( final String str ) {
975                String rtn = str ;
976                if( rtn != null && !rtn.isEmpty() ) {
977                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
978                        for( int i=0; i<rtn.length(); i++ ) {
979                                char ch = rtn.charAt(i) ;
980                                if( ch == '\\' ) {
981                                        final char ch2 = rtn.charAt(++i) ;              // ¥ の次の文字(n,r,tのみサポート)
982                                        switch( ch2 ) {
983                                                case 'n' : ch = '\n'; break;
984                                                case 'r' : ch = '\r'; break;
985                                                case 't' : ch = '\t'; break;
986                                                default  :
987                                                        final String errMsg = getClass().getName()
988                                                                                         + "の置換文字列で、「\\" + ch2 + "」は、サポートされていません。";
989                                                        System.err.println( errMsg );
990                                                        break;
991                                        }
992                                }
993                                buf.append( ch );
994                        }
995                        rtn = buf.toString();
996                }
997                return rtn ;
998        }
999
1000        /**
1001         * 【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[新規モード])。
1002         *
1003         * @og.tag
1004         * ファイルを書き込む場合、追加モードで書き込むかどうかをセットします。
1005         * 新規モード(true)の場合、既存のファイルが存在し、かつ書き込み許可があれば、
1006         * 上書きで新規に作成します。
1007         * 初期値は、false(新規モード)です。
1008         *
1009         * @param   flag 追加モード [true:追加モード/false:新規モード]
1010         */
1011        public void setFileAppend( final String flag ) {
1012                fileAppend = nval( getRequestParameter( flag ),fileAppend );
1013        }
1014
1015        /**
1016         * 【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false)。
1017         *
1018         * @og.tag
1019         * 大量に抜き出す場合、そのまま、サーバーから取り出すだけでも大変です。
1020         * zip 属性を、true にすると、GZIP で圧縮したファイルを作成します。
1021         * 初期値は、false(圧縮しない)です。
1022         *
1023         * @param  flag ZIP圧縮 [true:する/それ以外:しない]
1024         * @see    #setZipFilename( String )
1025         */
1026        public void setZip( final String flag ) {
1027                zip = nval( getRequestParameter( flag ),zip );
1028        }
1029
1030        /**
1031         * 【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip")。
1032         *
1033         * @og.tag
1034         * zip 属性に、true を指定した場合に、ZIPファイル化します。その場合のファイル名を指定します。
1035         * なにも指定しない場合は、filename + ".zip" になります。
1036         *
1037         * @param   zipFile ZIPファイル名
1038         * @see #setZip( String )
1039         */
1040        public void setZipFilename( final String zipFile ) {
1041                zipFilename = nval( getRequestParameter( zipFile ),zipFilename );
1042        }
1043
1044        /**
1045         * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します
1046         *              (初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
1047         *
1048         * @og.tag
1049         * ここでは、検索結果の件数や登録された件数をまず出力し、
1050         * その次に、ここで指定したメッセージをリソースから取得して
1051         * 表示します。
1052         * 件数を表示させる場合は、displayMsg = "MSG0033"[ 件検索しました] をセットしてください。
1053         * 表示させたくない場合は, displayMsg = "" をセットしてください。
1054         * (初期値:システム定数のVIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
1055         *
1056         * @param       id 結果表示メッセージID
1057         */
1058        public void setDisplayMsg( final String id ) {
1059                final String ids = getRequestParameter( id );
1060                if( ids != null ) { displayMsg = ids; }
1061        }
1062
1063        /**
1064         * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。
1065         *
1066         * @og.tag
1067         * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。
1068         * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、
1069         * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。
1070         * 表示させたくない場合は, notfoundMsg = "" をセットしてください。
1071         * 初期値は、MSG0077[対象データはありませんでした]です。
1072         *
1073         * @param       id ゼロ件時表示メッセージID
1074         */
1075        public void setNotfoundMsg( final String id ) {
1076                final String ids = getRequestParameter( id );
1077                if( ids != null ) { notfoundMsg = ids; }
1078        }
1079
1080        /**
1081         * 【TAG】(通常は使いません)データのフェッチサイズを指定します
1082         *              (初期値:DB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。
1083         *
1084         * @og.tag
1085         * より多くの行が必要なときに、データベースから取り出す必要がある行数に
1086         * ついてのヒントを JDBC ドライバに提供します。
1087         * 指定された行数は、この Statement を使って作成された結果セットにだけ影響します。
1088         * 指定された値が 0 の場合、ヒントは無視されます。
1089         * (初期値:システム定数のDB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。
1090         *
1091         * @param       size フェッチ行数
1092         */
1093        public void setFetchSize( final String size ) {
1094                fetchSize = nval( getRequestParameter( size ),fetchSize );
1095        }
1096
1097        /**
1098         * 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します。
1099         *
1100         * @og.tag
1101         * 複数ある場合は、CSV形式で渡します。
1102         * PL/SQL を使用しない場合は、無視されます。
1103         *
1104         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
1105         *
1106         * @param       nm 引数の名称 (CSV形式)
1107         */
1108        public void setNames( final String nm ) {
1109                names = nval( getRequestParameter( nm ),names );
1110        }
1111
1112        /**
1113         * 【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC})。
1114         *
1115         * @og.tag
1116         * ストアドプロシージャ等を実行する場合に、queryType="JDBCErrMsg" を
1117         * 指定する必要があります。(それ以外の指定は、初期値の JDBC になります。)
1118         * 初期値は、"JDBC" です。
1119         * {@og.doc03Link queryType Query_**** クラス}
1120         *
1121         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
1122         *
1123         * @param       id Query発行クラスID
1124         */
1125        public void setQueryType( final String id ) {
1126                // 内部的には、JDBCErrMsg:false / それ以外:true で管理しています。
1127                queryType = ! "JDBCErrMsg".equalsIgnoreCase( getRequestParameter( id ) );
1128        }
1129
1130        /**
1131         * 【TAG】ファイル出力時に、行番号情報を、出力する/しない[true/false]を指定します(初期値:true)。
1132         *
1133         * @og.tag
1134         * 通常のフォーマットでは、各行の先頭に行番号を出力します。
1135         * これは、#NAME 属性を使用する場合には、必ず出力する必要があります。
1136         * (#NAME 属性は、読み取り時にあれば、自動的にカラムに割り当てられます。)
1137         * この、先頭の行番号が不要な場合(つまり、他のシステムへのデータ出力等)
1138         * の為に出力する場合に、false を設定することで、行番号列を出力しない
1139         * ようにできます。
1140         * 初期値は、true(出力する) です。
1141         *
1142         * @og.rev 5.5.7.1 (2012/10/05) 新規追加
1143         * @param  flag 行番号出力 [true:する/それ以外:しない]
1144         */
1145        public void setUseNumber( final String flag ) {
1146                useNumber = nval( getRequestParameter( flag ),useNumber );
1147        }
1148
1149        /**
1150         * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します
1151         *              (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。
1152         *
1153         * @og.tag
1154         * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに
1155         * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。
1156         * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、
1157         * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、
1158         * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。
1159         * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
1160         * 初期値は、SystemData#USE_SQL_INJECTION_CHECK です。
1161         *
1162         * @og.rev 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
1163         *
1164         * @param   flag クォートチェック [true:する/それ以外:しない]
1165         */
1166        public void setQuotCheck( final String flag ) {
1167                quotCheck = nval( getRequestParameter( flag ),quotCheck );
1168        }
1169
1170        /**
1171         * 【TAG】リクエスト情報の HTMLTag開始/終了文字(&gt;&lt;) 存在チェックを実施するかどうか[true/false]を設定します
1172         *              (初期値:USE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。
1173         *
1174         * @og.tag
1175         * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
1176         * (&gt;&lt;) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
1177         * (初期値:システム定数のUSE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])
1178         *
1179         * @og.rev 6.2.2.0 (2015/03/27) XSSチェック,クォートチェック をサポートします。
1180         *
1181         * @param       flag    XSSチェック [true:する/false:しない]
1182         * @see         org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
1183         */
1184        public void setXssCheck( final String flag ) {
1185                xssCheck = nval( getRequestParameter( flag ),xssCheck );
1186        }
1187
1188        /**
1189         * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します
1190         *              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
1191         *
1192         * @og.tag
1193         * true に設定すると、処理時間を表示するバーイメージが表示されます。
1194         * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで
1195         * 表示させる機能です。処理時間の目安になります。
1196         * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
1197         *
1198         * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。
1199         *
1200         * @param       flag    処理時間を表示 [true:する/false:しない]
1201         */
1202        public void setUseTimeView( final String flag ) {
1203                useTimeView = nval( getRequestParameter( flag ),useTimeView );
1204        }
1205
1206        /**
1207         * 【TAG】エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
1208         *
1209         * @og.tag
1210         * 通常のエラーメッセージは、ラベル(長)が使われますが、これをラベル(短)を使いたい場合に、true にセットします。
1211         * ここでのラベル(短)は、タグ修飾なしの、ラベル(短)です。
1212         * 標準はfalse:利用しない=ラベル(長)です。
1213         * true/false以外を指定した場合はfalse扱いとします。
1214         *
1215         * ラベルリソースの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
1216         *
1217         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
1218         *
1219         * @param prm SLABEL利用 [true:利用する/false:利用しない]
1220         */
1221        public void setUseSLabel( final String prm ) {
1222                useSLabel = nval( getRequestParameter( prm ),useSLabel );
1223        }
1224
1225        /**
1226         * 【TAG】システム定数でクラウド設定されていても、クラウド環境を使用しない場合、trueを指定します(初期値:false)。
1227         *
1228         * @og.tag
1229         * クラウド設定は、システム定数の『CLOUD_TARGET』と『CLOUD_BUCKET』の設定で自動的に使用しますが、
1230         * どうしてもローカルでのみ使いたい場合は、この属性を true に設定します。
1231         * 標準はfalse:設定どおりとなります。
1232         *
1233         * true/false以外を指定した場合はfalse扱いとします。
1234         *
1235         * @og.rev 8.0.1.0 (2021/10/29) useLocal 属性を追加。
1236         *
1237         * @param flag ローカル環境のみ [true:ローカルのみ/false:設定どおり]
1238         */
1239        public void setUseLocal( final String flag ) {
1240                useLocal = nval( getRequestParameter( flag ),useLocal );
1241        }
1242
1243        /**
1244         * 【TAG】valueタグのaction=MAPOBJ を使用したラベル変換を行う場合の、MAPOBJキーを指定します。
1245         *
1246         * @og.tag
1247         *
1248         * DirectWriteTableTagでは、SQL文から直接ファイルを作成するため、headLabel はresource から
1249         * 取得します。
1250         * 通常の DBTableModel を作成する場合には、ColumnEditorTag でuseLabelMapとしてSQL文でキーとラベルの
1251         * Mapを作成してラベルの書き換えが行えますが、それと同等の事を行うためには、一旦、valueタグで、
1252         * command="SQL" action=MAPOBJ で、キーとラベルのMapを作成して、ラベルとして使用します。
1253         * なお、Mapオブジェクトは、scope="request" でのみやり取り可能です。
1254         *
1255         * @og.rev 7.0.7.1 (2019/12/24) mapObjKey 属性を追加。
1256         *
1257         * @param key valueタグで作成したMAPOBJを取り出すときのキー
1258         */
1259        public void setMapObjKey( final String key ) {
1260                mapObjKey = nval( getRequestParameter( key ),mapObjKey );
1261        }
1262
1263        /**
1264         * このオブジェクトの文字列表現を返します。
1265         * 基本的にデバッグ目的に使用します。
1266         *
1267         * @return このクラスの文字列表現
1268         * @og.rtnNotNull
1269         */
1270        @Override
1271        public String toString() {
1272                return ToString.title( this.getClass().getName() )
1273                                .println( "VERSION"             ,VERSION        )
1274                                .println( "dbid"                ,dbid           )
1275                                .println( "separator"   ,separator      )
1276                                .println( "useHeader"   ,useHeader      )
1277                                .println( "fileURL"             ,fileURL        )
1278                                .println( "filename"    ,filename       )
1279                                .println( "sql"                 ,sql            )
1280                                .println( "encode"              ,encode         )
1281                                .println( "fileAppend"  ,fileAppend     )
1282                                .println( "zip"                 ,zip            )
1283                                .println( "zipFilename" ,zipFilename)
1284                                .println( "displayMsg"  ,displayMsg     )
1285                                .println( "fetchSize"   ,fetchSize      )
1286                                .println( "queryType"   ,queryType      )
1287                                .println( "names"               ,names          )
1288                                .println( "errCode"             ,errCode        )
1289                                .println( "Other..."    ,getAttributes().getAttribute() )
1290                                .fixForm().toString() ;
1291        }
1292}