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.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.hayabusa.db.DBTableModel;
021import org.opengion.fukurou.util.ErrorMessage;
022import org.opengion.fukurou.util.FileUtil;
023import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
024import org.opengion.fukurou.util.ArraySet;                                              // 6.4.3.4 (2016/03/11)
025import org.opengion.fukurou.util.StringUtil;                                    // 7.2.7.0 (2020/08/07)
026
027import static org.opengion.fukurou.util.StringUtil.nval ;
028import static org.opengion.fukurou.system.HybsConst.BR;                 // 6.1.0.0 (2014/12/26) refactoring
029
030import java.util.Locale ;
031import java.util.Set ;
032// import java.util.TreeSet ;
033// import java.util.Comparator ;
034import java.io.File ;
035// import java.io.Serializable;
036import java.io.IOException ;                                                                    // 7.0.5.0 (2019/09/13)
037
038/**
039 * ファイル検索リストを元に、action に基づいた処理を行うタグです。
040 * command="ENTRY" 時のみ処理を行います。
041 *
042 * fileQuery などで検索したファイル一覧のDBTableModel を元に、ファイルの
043 * コピー(COPY)、移動(MOVE,MODIFY)、削除(DELETE)などの処理を行います。
044 * 処理を行うオリジナルファイルは、PARENT,NAME というカラムでなければなりません。
045 *   ※ 7.2.7.0 (2020/08/07) parentClm,nameClm で指定可能になりました。
046 * このカラム名は、fileQuery の検索時には、必ず作成されるカラムです。
047 * また、各アクションに対応するターゲットファイルは、TO_PARENT,TO_NAME という
048 * カラムで指定するか、targetDir 属性を利用してフォルダを指定します。
049 * TO_PARENT(先フォルダ)と、TO_NAME(先ファイル名)は、処理に応じて、必要なカラムが
050 * あれば、自動的に処理します。
051 * つまり、TO_PARENT のみの場合は、ファイル名はオリジナルのまま、フォルダのみ変更します。
052 * 逆に、TO_NAME の場合は、フォルダはそのままで、ファイル名のみ指定します。
053 * 両方同時に指定することも可能です。
054 * targetDir 属性で指定する場合は、TO_PARENT のみに同じ値を設定した場合と同じになります。
055 * この属性を指定すると、TO_PARENT は無視されます。(TO_NAME は有効です。)
056 * COPY、MOVE(,MODIFY) の場合は、指定のフォルダに一括処理可能です。
057 * COPY、MOVE(,MODIFY) などの処理で、ターゲットフォルダが存在しないときに、作成するか、エラーにするかは
058 * createDir属性 で指定できます。初期値は、(true:作成する) です。
059 * これは、COPY先やMOVE(,MODIFY)先が存在している前提のシステムで、不要な箇所に間違ってフォルダを
060 * 自動作成されると困る場合に、(false:作成しない) とすれば、間違いに気づく確率が上がります。
061 *
062 *   ※ 7.2.7.0 (2020/08/07) PARENT,NAME,TO_PARENT,TO_NAME のカラム名を指定できるようにしました。
063 *      action にMKDIRS を追加しました。これは、TO_PARENT フォルダが作成できるかどうかを判定します。
064 *
065 * ※ このタグは、Transaction タグの対象ではありません。
066 *
067 * @og.formSample
068 * ●body:なし
069 * ●形式:
070 *      ・<og:fileUpdate
071 *          action        = "COPY|MOVE|MODIFY|DELETE|MKDIRS" アクション属性(必須)
072 *          command       = "[ENTRY]"                 ENTRY 時のみ実行します(初期値:ENTRY)
073 *          targetDir     = "[指定フォルダ]"          ターゲットとなるフォルダ
074 *          createDir     = "[true/false]"            ターゲットとなるフォルダがなければ作成する(true)かどうか(初期値:true)
075 *          tableId       = [HybsSystem.TBL_MDL_KEY]  DBTableModel を取り出すキー
076 *          displayMsg    = "MSG0040";                処理結果を表示します(初期値:「 件登録しました。」)
077 *          selectedAll   = "[false/true]"            データを全件選択済みとして処理する(true)かどうか指定(初期値:false)
078 *          keepTimeStamp = "[false/true]"            COPY,親違いMOVE(,MODIFY)の時にオリジナルのタイムスタンプを使用するかどうか(初期値:false)
079 *      />
080 *
081 *    [action属性(必須)]
082 *      COPY   オリジナルファイルを、ターゲット(TO_PARENT,TO_NAMEで指定)にコピーします。
083 *      MOVE   オリジナルファイルを、ターゲットに移動(COPY+DELETE)/名称変更(RENAME)します。
084 *      MODIFY (MOVE と同じ。エンジンの command を利用するための簡易action)
085 *      DELETE オリジナルファイルを削除します(ターゲット(TO_PARENT,TO_NAME)は、関係しません)。
086 *      MKDIRS ターゲット(TO_PARENTで指定)フォルダを作成します。(PARENT,NAME,TO_NAMEは、関係しません)。 7.2.7.0 (2020/08/07) 新規追加
087 *
088 * ●Tag定義:
089 *   <og:fileUpdate
090 *       action           ○【TAG】アクション[COPY|MOVE|MODIFY|DELETE|MKDIRS]をセットします(必須)。
091 *       command            【TAG】コマンド[ENTRY]をセットします(初期値:ENTRY)
092 *       targetDir          【TAG】ターゲットとなるフォルダを指定します
093 *       createDir          【TAG】ターゲットとなるフォルダがなければ、作成するかどうかを指定します(初期値:true)
094 *       parentClm          【TAG】7.2.7.0 (2020/08/07) fileQuery以外の場合の元親フォルダを示すカラム名を指定します(初期値:"PARENT")
095 *       nameClm            【TAG】7.2.7.0 (2020/08/07) fileQuery以外の場合の元ファイル名を示すカラム名を指定します(初期値:"NAME")
096 *       toParentClm        【TAG】7.2.7.0 (2020/08/07) fileQuery以外の場合の先親フォルダを示すカラム名を指定します(初期値:"TO_PARENT")
097 *       toNameClm          【TAG】7.2.7.0 (2020/08/07) fileQuery以外の場合の先ファイル名を示すカラム名を指定します(初期値:"TO_NAME")
098 *       tableId            【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
099 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/application]を指定します(初期値:session)
100 *       displayMsg         【TAG】処理結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0040[ 件登録しました])
101 *       actErrMsg          【TAG】7.2.7.0 (2020/08/07) action実行時のエラーメッセージのメッセージリソースIDを指定します(初期値:ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2})
102 *       selectedAll        【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
103 *       keepTimeStamp      【TAG】オリジナルのタイムスタンプを利用するかどうかを指定します(初期値:false)
104 *       inPath             【TAG】6.8.0.0 (2017/06/02) 入力共通パスを指定します(PARENTフォルダの共通部分、COPY/MOVE時にtargetDirと置換されます) 。
105 *       useTimeView        【TAG】処理時間を表示する TimeView を表示するかどうかを指定します
106 *                                      (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
107 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
108 *       useStop            【TAG】7.2.7.0 (2020/08/07) エラー時に処理後に停止するかどうか[true/false]を指定します(初期値:true)
109 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
110 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
111 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
112 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
113 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
114 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
115 *   />
116 *
117 * ●使用例
118 *       ・<og:fileUpdate command="{@command}" action="COPY" />
119 *             TO_PARENT または、 TO_NAME(両方指定も可)による行単位 COPY 処理
120 *             fileQuery の useUpdateClm="true" を設定し、検索結果に、TO_PARENT、 TO_NAMEカラムを追加します。
121 *             TO_PARENT または、 TO_NAME は、columnSet などで値をセットしておきます。
122 *
123 *       ・<og:fileUpdate command="{@command}" action="MODIFY" targetDir="AAA_DIR"  />
124 *             fileQuery の検索結果を、AAA_DIR フォルダに移動します。
125 *             ファイル名は、そのままオリジナルの値が使用されます。
126 *
127 * @og.rev 5.3.4.0 (2011/04/01) 新規追加
128 * @og.group ファイル出力
129 *
130 * @version  4.0
131 * @author       Kazuhiko Hasegawa
132 * @since    JDK5.0,
133 */
134public class FileUpdateTag extends CommonTagSupport {
135        /** このプログラムのVERSION文字列を設定します。   {@value} */
136        private static final String VERSION = "7.2.9.4 (2020/11/20)" ;
137        private static final long serialVersionUID = 729420201120L ;
138
139        /** command 引数に渡す事の出来る コマンド  登録{@value} */
140        public static final String CMD_ENTRY  = "ENTRY" ;
141        /** command 引数に渡す事の出来る コマンド リスト  */
142        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
143        private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_ENTRY );
144
145        /** エラーメッセージID {@value} */
146        private static final String ERR_MSG_ID  = HybsSystem.ERR_MSG_KEY;               // 6.4.1.1 (2016/01/16) errMsgId → ERR_MSG_ID  refactoring
147
148        /** action 引数に渡す事の出来る アクションコマンド  COPY {@value} */
149        public static final String ACT_COPY             = "COPY" ;
150        /** action 引数に渡す事の出来る アクションコマンド  MOVE {@value} */
151        public static final String ACT_MOVE             = "MOVE" ;
152        /** action 引数に渡す事の出来る アクションコマンド  MODIFY {@value} */
153        public static final String ACT_MODIFY   = "MODIFY" ;
154        /** action 引数に渡す事の出来る アクションコマンド  DELETE {@value} */
155        public static final String ACT_DELETE   = "DELETE" ;
156        /** action 引数に渡す事の出来る アクションコマンド  MKDIRS {@value} */
157        public static final String ACT_MKDIRS   = "MKDIRS" ;                                            // 7.2.7.0 (2020/08/07)
158        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
159//      private static final Set<String> ACTION_SET = new ArraySet<>( ACT_COPY , ACT_MOVE , ACT_MODIFY , ACT_DELETE );
160        private static final Set<String> ACTION_SET = new ArraySet<>( ACT_COPY , ACT_MOVE , ACT_MODIFY , ACT_DELETE ,ACT_MKDIRS );      // 7.2.7.0 (2020/08/07)
161
162        private String  action          ;
163        private String  targetDir       ;                       // ターゲットとなるフォルダ
164        private boolean createDir       = true;         // ターゲットとなるフォルダがなければ、作成するかどうか(true:作成する)
165
166        private String  inPath          ;                       // 6.8.0.0 (2017/06/02) 入力共通パスを指定します。
167
168        private String  parentClm       = "PARENT";             // 7.2.7.0 (2020/08/07) fileQuery以外の場合の元親フォルダを示すカラム名を指定します(初期値:"PARENT")
169        private String  nameClm         = "NAME";               // 7.2.7.0 (2020/08/07) fileQuery以外の場合の元ファイル名を示すカラム名を指定します(初期値:"NAME")
170        private String  toParentClm     = "TO_PARENT";  // 7.2.7.0 (2020/08/07) fileQuery以外の場合の先親フォルダを示すカラム名を指定します(初期値:"TO_PARENT")
171        private String  toNameClm       = "TO_NAME";    // 7.2.7.0 (2020/08/07) fileQuery以外の場合の先ファイル名を示すカラム名を指定します(初期値:"TO_NAME")
172
173        private String  tableId         = HybsSystem.TBL_MDL_KEY;
174        private String  command         = CMD_ENTRY;
175//      private boolean outMessage      = true;                         // 7.2.7.0 (2020/08/07) 削除
176        private String  displayMsg      = "MSG0040";            //  件登録しました。
177        private String  actErrMsg       = "ERR0050";            // 7.2.7.0 (2020/08/07) アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
178
179        private boolean selectedAll ;
180        private boolean keepTimeStamp;                                  // オリジナルのタイムスタンプを利用する場合、true
181
182        private transient DBTableModel  table           ;
183        private transient ErrorMessage  errMessage      ;
184        private int             executeCount    = -1;                   // 処理件数
185        private int             errCode                 = ErrorMessage.OK;
186        private boolean useTimeView             = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );             // 6.3.6.0 (2015/08/16)
187        private boolean useSLabel               ;                               // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
188        private boolean useStop                 = true;                 // 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか(useStop対応)
189
190        /**
191         * デフォルトコンストラクター
192         *
193         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
194         */
195        public FileUpdateTag() { super(); }             // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
196
197        /**
198         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
199         *
200         * @og.rev 6.4.4.1 (2016/03/18) 意味のない、StringBuilderだったので、廃止します。
201         * @og.rev 6.9.9.0 (2018/08/20) 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。
202         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
203         * @og.rev 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか(useStop対応)
204         *
205         * @return      後続処理の指示
206         */
207        @Override
208        public int doEndTag() {
209                debugPrint();
210                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
211                if( !useTag() ) { return EVAL_PAGE ; }
212
213                final long dyStart = System.currentTimeMillis();
214
215                table = (DBTableModel)getObject( tableId );
216
217                // 7.2.7.0 (2020/08/07) 出力のタイミングを、useStop の判定前に持ってきます。
218//              String label  = "";                             // 4.0.0 (2005/11/30) 検索しなかった場合。
219                if( table != null && table.getRowCount() > 0 && check( command, COMMAND_SET ) ) {
220                        startQueryTransaction( tableId );
221
222                        execute();      // 実際の処理を実行します。
223
224                        setRequestAttribute( "DB.COUNT"   , String.valueOf( executeCount ) );
225                        setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
226
227                        // 6.9.9.0 (2018/08/20) 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。
228                        if( ! commitTableObject( tableId, table ) ) {
229                                if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Query Error!" ); }
230                                // ERR0041:検索処理中に割り込みの検索要求がありました。処理されません。
231                                errMessage.addMessage( 0,ErrorMessage.NG,"ERR0041" );
232                                errCode = ErrorMessage.NG;
233                        }
234
235//                      final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
236                        final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource(),useSLabel );         // 7.0.7.0 (2019/12/13)
237                        if( err != null && err.length() > 0 ) {
238                                // 7.2.7.0 (2020/08/07) 出力のタイミングを、useStop の判定前に持ってきます。
239//                              label = err ;           // 6.4.4.1 (2016/03/18)
240                                jspPrint( err );        // 7.2.7.0 (2020/08/07)
241                                setSessionAttribute( ERR_MSG_ID,errMessage );
242                        }
243
244                        // 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか
245                        if( useStop && errCode >= ErrorMessage.NG ) {
246                                return SKIP_PAGE ;
247                        }
248
249//                      // 6.9.9.0 (2018/08/20) 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。
250//                      if( table != null && ! commitTableObject( tableId, table ) ) {
251//                              jspPrint( "FileUpdateTag Query処理が割り込まれました。DBTableModel は登録しません。" );
252//                              return SKIP_PAGE ;
253//                      }
254                }
255
256//              jspPrint( label );                                              // 7.2.7.0 (2020/08/07)
257
258                // 実行件数の表示
259                // 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。
260                if( displayMsg != null && displayMsg.length() > 0 ) {
261                        final String status = executeCount + getResource().getLabel( displayMsg ) ;
262                        jspPrint( status + BR );
263                }
264
265                if( useTimeView ) {             // 6.3.6.0 (2015/08/16)
266                        // 3.5.4.7 (2004/02/06)
267                        final long dyTime = System.currentTimeMillis()-dyStart;
268                        jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );  // 3.5.6.3 (2004/07/12)
269                }
270                return EVAL_PAGE ;
271        }
272
273        /**
274         * タグリブオブジェクトをリリースします。
275         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
276         *
277         * @og.rev 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定します。
278         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
279         * @og.rev 7.2.7.0 (2020/08/07) outMessage 削除
280         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
281         * @og.rev 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか(useStop対応)
282         * @og.rev 7.2.7.0 (2020/08/07) ERR0050:アクション実行中にエラーが発生しました。
283         */
284        @Override
285        protected void release2() {
286                super.release2();
287                tableId         = HybsSystem.TBL_MDL_KEY;
288                command         = CMD_ENTRY;
289                action          = null;
290                targetDir       = null;                 // ターゲットとなるフォルダ
291                createDir       = true;                 // ターゲットとなるフォルダがなければ、作成するかどうか(true:作成する)
292//              outMessage      = true;
293                parentClm       = "PARENT";             // 7.2.7.0 (2020/08/07) fileQuery以外の場合の元親フォルダを示すカラム名を指定します(初期値:"PARENT")
294                nameClm         = "NAME";               // 7.2.7.0 (2020/08/07) fileQuery以外の場合の元ファイル名を示すカラム名を指定します(初期値:"NAME")
295                toParentClm     = "TO_PARENT";  // 7.2.7.0 (2020/08/07) fileQuery以外の場合の先親フォルダを示すカラム名を指定します(初期値:"TO_PARENT")
296                toNameClm       = "TO_NAME";    // 7.2.7.0 (2020/08/07) fileQuery以外の場合の先ファイル名を示すカラム名を指定します(初期値:"TO_NAME")
297                displayMsg      = "MSG0040";    //  件登録しました。
298                actErrMsg       = "ERR0050";    // 7.2.7.0 (2020/08/07) アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
299                selectedAll = false;
300                keepTimeStamp = false;          // オリジナルのタイムスタンプを利用する場合、true
301                table           = null;
302                errMessage      = null;
303                executeCount= -1;                       // 処理件数
304                errCode         = ErrorMessage.OK;
305                useTimeView     = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );     // 6.3.6.0 (2015/08/16)
306                inPath          = null;                 // 6.8.0.0 (2017/06/02) 入力共通パスを指定します。
307                useSLabel       = false;                // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
308                useStop         = true;                 // 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか
309        }
310
311        /**
312         * 処理を実行します。
313         *
314         * @og.rev 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定します。
315         * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
316         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
317         */
318        private void execute() {
319                final int[] rowNo = getParameterRows();
320                if( rowNo.length > 0 ) {
321                        // 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
322                        final String[] clms = new String[] { parentClm,nameClm,toParentClm,toNameClm } ;
323
324//                      final FromToFiles fromToFiles = new FromToFiles( table , targetDir , createDir , inPath );                      // 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定
325                        final FromToFiles fromToFiles = new FromToFiles( table , targetDir , createDir , inPath , clms );       // 7.2.7.0 (2020/08/07)
326
327                        if( ACT_COPY.equalsIgnoreCase( action ) ) {
328                                actionCOPY( rowNo,fromToFiles );
329                        }
330                        // ACT_MODIFY は、エンジンの command で使うため、便利
331                        else if( ACT_MOVE.equalsIgnoreCase( action ) || ACT_MODIFY.equalsIgnoreCase( action ) ) {
332                                actionMOVE( rowNo,fromToFiles );
333                        }
334                        else if( ACT_DELETE.equalsIgnoreCase( action ) ) {
335                                actionDELETE( rowNo,fromToFiles );
336                        }
337                        else if( ACT_MKDIRS.equalsIgnoreCase( action ) ) {              // 7.2.7.0 (2020/08/07)
338                                actionMKDIRS( rowNo,fromToFiles );
339                        }
340                }
341        }
342
343        /**
344         * COPY アクションを実行します。
345         *
346         * エラー発生時の引数は、アクション:{0} From:{1} To:{2} です。
347         *
348         * @og.rev 5.6.5.2 (2013/06/21) From側がファイルの場合のみ処理します。
349         * @og.rev 7.2.7.0 (2020/08/07) ERR0050:アクション実行中にエラーが発生しました。
350         *
351         * @param       rowNo           処理を実施する行番号
352         * @param       fromToFiles     FromFile,ToFileをまとめた補助クラス
353         * @throws      HybsSystemException     処理中に何らかのエラーが発生した場合
354         */
355        private void actionCOPY( final int[] rowNo , final FromToFiles fromToFiles ) {
356                File fromFile = null ;
357                File toFile   = null ;
358
359                executeCount = 0 ;      // 開始前に初期化しておく。
360                final int rowCount = rowNo.length ;
361                for( int i=0; i<rowCount; i++ ) {
362                        final File[] files = fromToFiles.makeFromToFile( rowNo[i] );    // FromFile,ToFile
363                        fromFile = files[0];
364                        toFile   = files[1];
365
366                        if( fromFile.isFile() && !FileUtil.copy( fromFile,toFile,keepTimeStamp ) ) {
367                                if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
368                                // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
369                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,fromFile.toString(),toFile.toString() );
370                                errCode = ErrorMessage.NG;
371                        }
372
373//                      // 5.6.5.2 (2013/06/21) From側がファイルの場合のみ処理します。
374//                      if( fromFile.isFile() && !FileUtil.copy( fromFile,toFile,keepTimeStamp ) ) {
375//                              final String errMsg = "アクション=[" + action + "]中にエラーが発生しました。" + CR
376//                                                                      + "From=[" + fromFile + "],To=[" + toFile + "]" + CR;
377//                              // 6.0.2.5 (2014/10/31) refactoring
378//                              if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
379//                              errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,"actionCOPY",errMsg );
380//                      }
381                        executeCount++ ;
382                }
383        }
384
385        /**
386         * MOVE アクションを実行します。
387         *
388         * エラー発生時の引数は、アクション:{0} From:{1} To:{2} です。
389         *
390         * @og.rev 5.5.2.4 (2012/05/16) メソッドの戻り値の設定
391         * @og.rev 5.6.5.2 (2013/06/21) From側がファイルの場合のみ処理します。
392         * @og.rev 7.2.7.0 (2020/08/07) ERR0050:アクション実行中にエラーが発生しました。
393         *
394         * @param       rowNo           処理を実施する行番号
395         * @param       fromToFiles     FromFile,ToFileをまとめた補助クラス
396         * @throws      HybsSystemException     処理中に何らかのエラーが発生した場合
397         */
398        private void actionMOVE( final int[] rowNo , final FromToFiles fromToFiles ) {
399                File fromFile = null ;
400                File toFile   = null ;
401
402                executeCount = 0 ;      // 開始前に初期化しておく。
403                final int rowCount = rowNo.length ;
404                for( int i=0; i<rowCount; i++ ) {
405                        final File[] files = fromToFiles.makeFromToFile( rowNo[i] );    // FromFile,ToFile
406                        fromFile = files[0];
407                        toFile   = files[1];
408
409                        if( fromToFiles.lastParentEquals() ) {  // FromDirとToDirが同じなので、RENAMEできる。
410                                if( !fromFile.renameTo( toFile ) ) {
411                                        if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
412                                        // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
413                                        errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,fromFile.toString(),toFile.toString() );
414                                        final String errMsg1 = "同一親フォルダのため、RENAME処理を行っています。";
415                                        errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,errMsg1 );
416                                        errCode = ErrorMessage.NG;
417
418//                                      final String errMsg = "アクション=[" + action + "]中にエラーが発生しました。" + CR
419//                                                                              + "同一親フォルダのため、RENAME処理を行っています。" + CR
420//                                                                              + "From=[" + fromFile + "],To=[" + toFile + "]" + CR;
421//                                      // 6.0.2.5 (2014/10/31) refactoring
422//                                      if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
423//                                      errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,"actionMOVE",errMsg );
424                                }
425                        }
426                        // 5.6.5.2 (2013/06/21) From側がファイルの場合のみ処理します。
427                        else if( fromFile.isFile() ) {                  // FromDirとToDirが異なるので、COPY + DELETE する。
428                                if( !FileUtil.copy( fromFile,toFile,keepTimeStamp ) ) {
429                                        if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
430                                        // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
431                                        errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,fromFile.toString(),toFile.toString() );
432                                        final String errMsg1 = "移動前のCOPY処理を行っていました。";
433                                        errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,errMsg1 );
434                                        errCode = ErrorMessage.NG;
435
436//                                      final String errMsg = "アクション=[" + action + "]中にエラーが発生しました。" + CR
437//                                                                              + "移動前のCOPY処理を行っていました。" + CR
438//                                                                              + "From=[" + fromFile + "],To=[" + toFile + "]" + CR;
439//                                      // 6.0.2.5 (2014/10/31) refactoring
440//                                      if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
441//                                      errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,"actionMOVE",errMsg );
442                                }
443
444                                if( !fromFile.delete() ) {
445                                        if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
446                                        // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
447                                        errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,fromFile.toString(),toFile.toString() );
448                                        final String errMsg1 = "移動後のオリジナルファイルの削除処理を行っていました。";
449                                        errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,errMsg1 );
450                                        errCode = ErrorMessage.NG;
451
452//                                      String errMsg = "アクション=[" + action + "]中にエラーが発生しました。" + CR
453//                                                                              + "移動後のオリジナルファイルの削除処理を行っていました。" + CR
454//                                                                              + "From=[" + fromFile + "],To=[" + toFile + "]" + CR;
455//                                      // 5.5.2.4 (2012/05/16) メソッドの戻り値の設定
456                                        if( !toFile.delete() ) {
457//                                              errMsg = errMsg + "toFile も削除に失敗しました。" + CR;
458                                                final String errMsg3 = "toFile も削除に失敗しました。";
459                                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,errMsg3 );
460                                        }
461
462//                                      // 6.0.2.5 (2014/10/31) refactoring
463//                                      if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
464//                                      errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,"actionMOVE",errMsg );
465                                }
466                        }
467                        executeCount++ ;
468                }
469        }
470
471        /**
472         * DELETE アクションを実行します。
473         *
474         * この処理では、リストにフォルダが含まれている場合も削除します。
475         * 通常、フォルダの削除は、その要素(内部)にファイル等が存在しない場合のみ
476         * 行いますが、検索リストから削除する順番によっては、フォルダもファイルも
477         * 削除対象になる場合があります。そこで、まず。ファイルだけ削除し、フォルダは、
478         * あとで削除するように処理を行います。
479         *
480         * エラー発生時の引数は、アクション:{0} From:{1} To:"" です。
481         *
482         * @og.rev 5.6.5.2 (2013/06/21) フォルダも削除対象にします。
483         * @og.rev 7.2.6.0 (2020/06/30) 削除方法を変更します。
484         * @og.rev 7.2.7.0 (2020/08/07) ERR0050:アクション実行中にエラーが発生しました。
485         *
486         * @param       rowNo           処理を実施する行番号
487         * @param       fromToFiles     FromFile,ToFileをまとめた補助クラス
488         * @throws      HybsSystemException     処理中に何らかのエラーが発生した場合
489         */
490        private void actionDELETE( final int[] rowNo , final FromToFiles fromToFiles ) {
491                final int rowCount = rowNo.length ;
492                for( int i=0; i<rowCount; i++ ) {
493                        final File fmFile = fromToFiles.makeFromOnly( rowNo[i] );       // FromFile
494                        try {
495                                delete( fmFile );
496                                executeCount++ ;
497                        }
498                        catch( final Throwable th ) {
499                                if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
500                                // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
501                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,fmFile.toString(),"" );
502                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,th.getMessage() );
503                                errCode = ErrorMessage.NG;
504
505//                              final String errMsg = "アクション=[" + action + "]中にエラーが発生しました。" + CR
506//                                                                      + "From=[" + fmFile + "]" + CR;
507//                              // 6.0.2.5 (2014/10/31) refactoring
508//                              if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
509//                              errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,"actionDELETE",errMsg );
510                        }
511                }
512        }
513
514        /**
515         * フォルダやファイルがあっても、すべて再帰的に削除します。
516         *
517         * @og.rev 7.2.6.0 (2020/06/30) 削除方法を変更します。
518         * @og.rev 7.2.9.4 (2020/11/20) spotbugs:null になっている可能性があるメソッドの戻り値を利用している
519         *
520         * @param       inFile  起点となるフォルダ
521         */
522        private static void delete( final File inFile ) {
523                final File[] flist = inFile.listFiles();
524                if( flist != null ) {                                                           // 7.2.9.4 (2020/11/20)
525                        for( final File file : flist) {
526                                if( file.isDirectory() ) {
527                                        delete( file );
528                                } else {
529                                        file.delete();
530                                }
531                        }
532                }
533                inFile.delete();
534        }
535
536        /**
537         * MKDIRS アクションを実行します。
538         *
539         * この処理では、TO_PARENT カラムに指定されたターゲットフォルダを 多階層mkdirします。
540         * 実際は、ファイル作成やコピー時には、mkdirs するので、ここでの使い方の想定は、
541         * ターゲットフォルダへのアクセスが可能かどうかの確認です。
542         * 存在チェック(EXISTS)では、本当に存在しない場合も、アクセス出来ない場合も
543         * false となるためです。
544         * なお、通常の mkdirs では、フォルダが新規に作成されなかった場合(すでに存在)
545         * false となりますが、ここのアクションでは、アクセスチェックも兼ねているため、
546         * mkdirs が成功するか、すでに存在している場合は、正常と判断します。
547         *
548         * エラー発生時の引数は、アクション:{0} From:"" To:{2} です。
549         *
550         * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
551         *
552         * @param       rowNo           処理を実施する行番号
553         * @param       fromToFiles     FromFile,ToFileをまとめた補助クラス
554         * @throws      HybsSystemException     処理中に何らかのエラーが発生した場合
555         */
556        private void actionMKDIRS( final int[] rowNo , final FromToFiles fromToFiles ) {
557                final int rowCount = rowNo.length ;
558                for( int i=0; i<rowCount; i++ ) {
559                        try {
560                                fromToFiles.makeToParent( rowNo[i] );           // 戻り値は確認しない。
561                        }
562                        catch( final Throwable th ) {
563                                if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
564                                // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
565                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,"",fromToFiles.getLastDir() );
566                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,th.getMessage() );
567                                errCode = ErrorMessage.NG;
568                        }
569                }
570        }
571
572        /**
573         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。
574         *
575         * @return      選択行の配列
576         * @og.rtnNotNull
577         */
578        @Override
579        protected int[] getParameterRows() {
580                final int[] rowNo ;
581                if( selectedAll ) {
582                        final int rowCnt = table.getRowCount();
583                        rowNo = new int[ rowCnt ];
584                        for( int i=0; i<rowCnt; i++ ) {
585                                rowNo[i] = i;
586                        }
587                } else {
588                        rowNo = super.getParameterRows();
589                }
590                return rowNo ;
591        }
592
593        /**
594         * 【TAG】アクション[COPY|MOVE|MODIFY|DELETE|MKDIRS]をセットします。
595         *
596         * @og.tag
597         * アクションは、ファイルをコピー(COPY)したり、移動(MOVE,MODIFY)したり、削除(DELETE)、
598         * フォルダ作成(MKDIRS)するなどの操作を指定する必須属性です。
599         *
600         * <table class="plain">
601         *   <caption>action属性(必須)のキーワード</caption>
602         *   <tr><th>action</th><th>名称</th><th>機能</th></tr>
603         *   <tr><td>COPY  </td><td>コピー</td><td>オリジナルファイルを、ターゲット(TO_PARENT,TO_NAMEで指定)にコピーします。</td></tr>
604         *   <tr><td>MOVE  </td><td>移動  </td><td>オリジナルファイルを、ターゲットに移動(COPY+DELETE)/名称変更(RENAME)します。</td></tr>
605         *   <tr><td>MODIFY</td><td>移動  </td><td>(MOVE と同じ。エンジンの command を利用するための簡易action)</td></tr>
606         *   <tr><td>DELETE</td><td>削除  </td><td>オリジナルファイルを、削除します。(フォルダ、ファイルに関わらず)</td></tr>
607         *   <tr><td>MKDIRS</td><td>削除  </td><td>ターゲット(TO_PARENTで指定)フォルダを作成します。 7.2.7.0 (2020/08/07)</td></tr>
608         * </table>
609         *
610         * @og.rev 6.3.4.0 (2015/08/01) Arrays.toString から String.join に置き換え。
611         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
612         *
613         * @param       act アクション (public static final 宣言されている文字列)
614         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.FileUpdateTag.ACT_COPY">アクション定数</a>
615         */
616        public void setAction( final String act ) {
617                action = nval( getRequestParameter( act ),action );
618
619                if( action != null && !check( action, ACTION_SET ) ) {
620                        final String errMsg = "指定のアクションは実行できません。アクションエラー"       + CR
621                                                        + "action=[" + action + "] "                                                            + CR
622                                                        + "actionList=" + String.join( ", " , ACTION_SET ) ;
623                        throw new HybsSystemException( errMsg );
624                }
625        }
626
627        /**
628         * 【TAG】ターゲットとなるフォルダを指定します(初期値:null)。
629         *
630         * @og.tag
631         * targetDir 属性を利用する場合は、引数のファイル、またはフォルダが指定されたことに
632         * なります。COPY、MOVE(,MODIFY) の場合は、targetDir 属性にフォルダを指定することで一括処理可能です。
633         * 指定先のフォルダが存在しない場合は、createDir属性の値により処理が異なります。
634         * createDir="true"(初期値)で、ターゲットフォルダが存在しない場合は、自動作成します。
635         *
636         * @param  dir ターゲットとなるフォルダ
637         * @see         #setCreateDir( String )
638         */
639        public void setTargetDir( final String dir ) {
640                targetDir = nval( getRequestParameter( dir ),targetDir );
641        }
642
643        /**
644         * 【TAG】ターゲットとなるフォルダがなければ、作成するかどうかを指定します(初期値:true)。
645         *
646         * @og.tag
647         * COPY,MOVE(,MODIFY) などの処理で、ターゲットフォルダが存在しないときに、作成するか、エラーにするかを
648         * createDir属性 で指定できます。
649         * これは、COPY先やMOVE(,MODIFY)先が存在している前提のシステムで、不要な箇所に間違ってフォルダを
650         * 自動作成されると困る場合に、false:作成しない とすれば、間違いに気づく確率が上がります。
651         * 初期値は true:作成する です。
652         *
653         * @param       flag    フォルダ作成可否 [true:作成する/false:作成しない]
654         */
655        public void setCreateDir( final String flag ) {
656                createDir = nval( getRequestParameter( flag ),createDir );
657        }
658
659        /**
660         * 【TAG】fileQuery以外の場合の元親フォルダを示すカラム名を指定します(初期値:"PARENT")。
661         *
662         * @og.tag
663         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
664         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
665         * それぞれのカラム名を指定できるようにします。
666         * (初期値:"PARENT")
667         *
668         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
669         *
670         * @param       clm 元親フォルダを示すカラム名
671         */
672        public void setParentClm( final String clm ) {
673                parentClm = nval( getRequestParameter( clm ),parentClm );
674        }
675
676        /**
677         * 【TAG】fileQuery以外の場合の元ファイル名を示すカラム名を指定します(初期値:"NAME")。
678         *
679         * @og.tag
680         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
681         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
682         * それぞれのカラム名を指定できるようにします。
683         * (初期値:"NAME")
684         *
685         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
686         *
687         * @param       clm 元ファイル名を示すカラム名
688         */
689        public void setNameClm( final String clm ) {
690                nameClm = nval( getRequestParameter( clm ),nameClm );
691        }
692
693        /**
694         * 【TAG】fileQuery以外の場合の先親フォルダを示すカラム名を指定します(初期値:"TO_PARENT")。
695         *
696         * @og.tag
697         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
698         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
699         * それぞれのカラム名を指定できるようにします。
700         * (初期値:"TO_PARENT")
701         *
702         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
703         *
704         * @param       clm 先親フォルダを示すカラム名
705         */
706        public void setToParentClm( final String clm ) {
707                toParentClm = nval( getRequestParameter( clm ),toParentClm );
708        }
709
710        /**
711         * 【TAG】fileQuery以外の場合の先ファイル名を示すカラム名を指定します(初期値:"TO_NAME")。
712         *
713         * @og.tag
714         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
715         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
716         * それぞれのカラム名を指定できるようにします。
717         * (初期値:"TO_NAME")
718         *
719         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
720         *
721         * @param       clm 先ファイル名を示すカラム名
722         */
723        public void setToNameClm( final String clm ) {
724                toNameClm = nval( getRequestParameter( clm ),toNameClm );
725        }
726
727        /**
728         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
729         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
730         *
731         * @og.tag
732         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
733         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
734         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
735         * この tableId 属性を利用して、メモリ空間を分けます。
736         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
737         *
738         * @param       id テーブルID (sessionに登録する時のID)
739         */
740        public void setTableId( final String id ) {
741                tableId = nval( getRequestParameter( id ),tableId );
742        }
743
744        /**
745         * 【TAG】コマンド (ENTRY)をセットします(初期値:ENTRY)。
746         *
747         * @og.tag
748         * このタグは、command="ENTRY" でのみ実行されます。
749         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
750         * フィールド定数値のいづれかを、指定できます。
751         * 初期値は、ENTRY なので、何も指定しなければ、実行されます。
752         *
753         * @param       cmd コマンド (public static final 宣言されている文字列)
754         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.FileUpdateTag.CMD_ENTRY">コマンド定数</a>
755         */
756        public void setCommand( final String cmd ) {
757                final String cmd2 = getRequestParameter( cmd );
758                if( cmd2 != null && cmd2.length() >= 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
759        }
760
761//      /**
762//       * 【TAG】検索結果のメッセージを表示する/しない[true/false]を指定します(初期値:true)。
763//       *
764//       * @og.tag
765//       * 初期値は、表示する:true です。
766//       *
767//       * @og.rev 7.2.7.0 (2020/08/07) outMessage 削除
768//       *
769//       * @param       flag  メッセージ表示可否 [true:表示する/それ以外:含めない]
770//       */
771//      public void setOutMessage( final String flag ) {
772//              outMessage = nval( getRequestParameter( flag ),outMessage );
773//      }
774
775        /**
776         * 【TAG】処理結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0040[ 件登録しました])。
777         *
778         * @og.tag
779         * ここでは、検索結果の件数や登録された件数をまず出力し、
780         * その次に、ここで指定したメッセージをリソースから取得して表示します。
781         * 表示させたくない場合は, displayMsg = "" をセットしてください。
782         * displayMsg の初期値は、MSG0040[ 件登録しました]です。
783         *
784         * @param       id 処理結果表示メッセージID
785         */
786        public void setDisplayMsg( final String id ) {
787                final String ids = getRequestParameter( id );
788                if( ids != null ) { displayMsg = ids; }
789        }
790
791        /**
792         * 【TAG】action実行時のエラーメッセージのメッセージリソースIDを指定します(初期値:ERR0050:アクション実行中にエラーが発生しました。
793         *
794         * @og.tag
795         * action実行時のエラーメッセージは、COPY|MOVE|MODIFY|DELETE|MKDIRS などのアクション実行時に
796         * エラーが発生した際に、表示するメッセージリソースのID をセットします。
797         * エラーメッセージのため、未指定にすることはできません。
798         * actErrMsg の初期値は、ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
799         *
800         * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
801         *
802         * @param       id action実行時のエラーメッセージのメッセージリソースID
803         */
804        public void setActErrMsg( final String id ) {
805                actErrMsg = nval( getRequestParameter( id ),actErrMsg );
806        }
807
808        /**
809         * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
810         *
811         * @og.tag
812         * 全てのデータを選択済みデータとして扱って処理します。
813         * 全件処理する場合に、(true/false)を指定します。
814         * 初期値は false です。
815         *
816         * @param  all 全件選択済み指定 [true:全件選択済み/false:通常]
817         */
818        public void setSelectedAll( final String all ) {
819                selectedAll = nval( getRequestParameter( all ),selectedAll );
820        }
821
822        /**
823         * 【TAG】オリジナルのタイムスタンプを利用するかどうかを指定します(初期値:false)。
824         *
825         * @og.tag
826         * COPYや親違いMOVE(,MODIFY)の時に、オリジナルのタイムスタンプをそのままコピー先のファイルにも
827         * 適用するかどうかを指定します。
828         * タイムスタンプを初期化されたくない場合に、true に設定します。
829         * 初期値は 利用しない:false です。
830         *
831         * @param  flag タイムスタンプ利用 [true:する/false:しない]
832         */
833        public void setKeepTimeStamp( final String flag ) {
834                keepTimeStamp = nval( getRequestParameter( flag ),keepTimeStamp );
835        }
836
837        /**
838         * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します
839         *              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
840         *
841         * @og.tag
842         * true に設定すると、処理時間を表示するバーイメージが表示されます。
843         * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで
844         * 表示させる機能です。処理時間の目安になります。
845         * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
846         *
847         * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。
848         *
849         * @param       flag    処理時間を表示 [true:する/false:しない]
850         */
851        public void setUseTimeView( final String flag ) {
852                useTimeView = nval( getRequestParameter( flag ),useTimeView );
853        }
854
855        /**
856         * 【TAG】入力共通パスを指定します。
857         *
858         * @og.tag
859         * 通常、fileQueryタグ等で、検索した結果は、PARENT,NAME というカラムにセットされます。
860         * この、fileQueryのfrom属性に、検索を開始するディレクトリを指定し、multi="true"で、
861         * 多段階展開 した場合、この、from属性に指定したパスを、inPath にセットすることで、
862         * 以下の階層フォルダそのままに、targetDir にCOPY または、MOVE することが出来ます。
863         * 逆に、指定しない場合は、フォルダ階層無しで、COPY,MOVE されます。
864         *
865         * @og.rev 6.8.0.0 (2017/06/02) 入力共通パスを指定します。
866         * @og.rev 7.0.5.0 (2019/09/13) inPath のパスは、正規パス名から作成。
867         *
868         * @param       path    入力共通パス
869         */
870        public void setInPath( final String path ) {
871                inPath = nval( getRequestParameter( path ),inPath );
872
873                // 7.0.5.0 (2019/09/13) inPath のパスは、正規パス名から作成。
874                if( inPath != null ) {
875                        try {
876                                inPath = new File(inPath).getCanonicalPath();
877                        }
878                        catch( final IOException ex ) {
879                                final String errMsg = "inPathの正式なファイル名の取得に失敗しました。[" + inPath + "]"
880                                                        + CR + ex.getMessage();
881                                throw new HybsSystemException( errMsg,ex );
882                        }
883                }
884        }
885
886        /**
887         * 【TAG】エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
888         *
889         * @og.tag
890         * 通常のエラーメッセージは、ラベル(長)が使われますが、これをラベル(短)を使いたい場合に、true にセットします。
891         * ここでのラベル(短)は、タグ修飾なしの、ラベル(短)です。
892         * 標準はfalse:利用しない=ラベル(長)です。
893         * true/false以外を指定した場合はfalse扱いとします。
894         *
895         * ラベルリソースの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
896         *
897         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
898         *
899         * @param prm SLABEL利用 [true:利用する/false:利用しない]
900         */
901        public void setUseSLabel( final String prm ) {
902                useSLabel = nval( getRequestParameter( prm ),useSLabel );
903        }
904
905        /**
906         * 【TAG】エラー時に処理後に停止するかどうか[true/false]を指定します(初期値:true)。
907         *
908         * @og.tag
909         * 処理結果などに応じて、以下の処理を停止したい場合に、使用します。
910         * false を指定すると、判定結果に無関係に、以下の処理を実行します。
911         * 処理は継続したいが、警告表示する場合に、useStop="false" を指定します。
912         * 初期値は、停止する ("true")です。
913         *
914         * @og.rev 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか(useStop対応)
915         *
916         * @param   flag 処理後停止 [true:する/それ以外:しない]
917         */
918        public void setUseStop( final String flag ) {
919                useStop = nval( getRequestParameter( flag ),useStop );
920        }
921
922        /**
923         * DBTableModel から、FromFile,ToFile を作成するための処理をまとめた補助クラスです。
924         *
925         * ここでは、オリジナルファイルやターゲットファイルを作成するための処理のみを集めています。
926         * メソッドにすると、ローカル変数を多く管理するか、多数の引数渡しを繰り返すことになるため、
927         * このローカルクラスに処理と、値を格納します。
928         *
929         * @og.rev 7.2.7.0 (2020/08/07) 相対パスの場合の基準フォルダ(FILE_URL) 考慮
930         */
931        private static final class FromToFiles {
932                private final String fileURL = HybsSystem.sys( "FILE_URL" );            // 7.2.7.0 (2020/08/07) 相対パスの場合の基準フォルダ
933
934                private final DBTableModel      table ;
935
936                private final int PARENT        ;
937                private final int NAME          ;
938                private final int TO_PARENT     ;
939                private final int TO_NAME       ;
940
941                private final File              toDir   ;
942                private final boolean   createDir;              // ターゲットとなるフォルダがなければ、作成するかどうか(true:作成する)
943                private final int               inPathCnt;              // 6.8.0.0 (2017/06/02) 入力共通パスの文字数
944
945                private boolean equalParent     ;                       // 最後に実行された処理で、親フォルダが同一の場合は、true
946                private String  lastDir ;                               // 7.2.7.0 (2020/08/07) 一番最後にmkdirsしたフォルダ名(Exceptionの直前の設定値)
947
948                /**
949                 *  引数指定のコンストラクター
950                 *
951                 * 必要なパラメータを渡して、オブジェクトを構築します。
952                 * 入力共通パス(inPath)は、COPY/MOVE時にtargetDirと置換されます。
953                 *
954                 * @og.rev 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定します。
955                 * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
956                 *
957                 * @param       table     一覧が格納されているDBTableModel
958                 * @param       targetDir       ターゲットとなるフォルダ
959                 * @param       createDir       フォルダ作成可否 [true:作成する/false:作成しない]
960                 * @param       inPath          入力共通パス
961                 * @param       clms            parentClm,nameClm,toParentClm,toNameClm の配列
962                 */
963//              public FromToFiles( final DBTableModel table , final String targetDir , final boolean createDir , final String inPath ) {
964                public FromToFiles( final DBTableModel table , final String targetDir , final boolean createDir , final String inPath , final String[] clms ) {
965
966                        this.table              = table;
967                        this.createDir  = createDir ;
968                        toDir                   = mkDirs( targetDir,createDir );                        // targetDir が指定されていない場合は、null
969                        // 7.0.5.0 (2019/09/13) inPath のパスの文字数も、正規パス名から作成。
970                        inPathCnt               = inPath == null ? 0 : inPath.length() ;        // 6.8.0.0 (2017/06/02) 入力共通パスの文字数
971
972                        // "PARENT","NAME","TO_PARENT","TO_NAME" のカラム名のDBTableModelのカラム番号。存在しない場合は、-1
973//                      PARENT          = table.getColumnNo( "PARENT"   , false );
974//                      NAME            = table.getColumnNo( "NAME"             , false );
975//                      TO_PARENT       = table.getColumnNo( "TO_PARENT", false );
976//                      TO_NAME         = table.getColumnNo( "TO_NAME"  , false );
977
978                        PARENT          = table.getColumnNo( clms[0]    , false );              // 7.2.7.0 (2020/08/07) parentClm
979                        NAME            = table.getColumnNo( clms[1]    , false );              // 7.2.7.0 (2020/08/07) nameClm
980                        TO_PARENT       = table.getColumnNo( clms[2]    , false );              // 7.2.7.0 (2020/08/07) toParentClm
981                        TO_NAME         = table.getColumnNo( clms[3]    , false );              // 7.2.7.0 (2020/08/07) toNameClm
982                }
983
984                /**
985                 * 行番号より、対応するオリジナルファイル(FromFile)を返します。
986                 *
987                 * ここでは、TO_PARENT や TO_NAME は、判定する必要がないため、makeFromToFile( int ) の
988                 * 一部のみで処理が終了できます。
989                 * 1.FromDir は、PARENT 列の値から作成する。
990                 * 2.FromFileは、FromDir + NAME列の値から作成する。
991                 *
992                 * 配列返しの関係で、メソッド(および内部処理)を分けています。
993                 *
994                 * @param       rowNo   カラムNo
995                 * @return      オリジナルファイル(FromFile)
996                 * @og.rtnNotNull
997                 * @see         #makeFromToFile( int )
998                 */
999                public File makeFromOnly( final int rowNo ) {
1000                        final String[] value = table.getValues( rowNo );
1001                        final File fromDir  = mkDirs( value[PARENT],createDir );
1002
1003                        return new File( fromDir, value[NAME] ) ;
1004                }
1005
1006                /**
1007                 * 行番号より、対応するTO_PARENT フォルダのFileオブジェクトを返します。
1008                 *
1009                 * ここでは、TO_PARENT のみで処理します。
1010                 *
1011                 * 実質的には、MKDIRSアクション でしか使用しないので、Fileオブジェクトを作成する必要すら
1012                 * ありませんが、メソッド共通化の関係で、オリジナルをそのまま使用します。
1013                 *
1014                 * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
1015                 *
1016                 * @param       rowNo   カラムNo
1017                 * @return      TO_PARENT フォルダのFileオブジェクト(カラムが存在しない場合は、null)
1018                 */
1019                public File makeToParent( final int rowNo ) {
1020                        final File rtnFile ;
1021                        if( TO_PARENT >= 0 ) {
1022                                final String[] value = table.getValues( rowNo );
1023                                rtnFile = mkDirs( value[TO_PARENT],true );
1024                        }
1025                        else {
1026                                rtnFile = null;
1027                        }
1028                        return rtnFile;
1029                }
1030
1031                /**
1032                 * 行番号より、対応するオリジナルファイル(FromFile)とターゲットファイル(ToFile)を配列に格納して返します。
1033                 *
1034                 * ここでは、TO_PARENT や TO_NAME は、存在するかどうか不明なので、以下の手順で作成します。
1035                 * 1.FromDir は、PARENT 列の値から作成する。
1036                 * 2.FromFileは、FromDir + NAME列の値から作成する。
1037                 * 3.toDir は、
1038                 *       A.targetDir が有れば、それを使う。
1039                 *       B.なければ、TO_PARENT 列の値から作成する。
1040                 *       C.TO_PARENT 列がないか、値が未設定の場合は、FromDir をそのまま使う。
1041                 * 4.toFile は、
1042                 *       A.toDir + TO_NAME 列の値から作成する。
1043                 *       B.TO_NAME 列がないか、値が未設定の場合は、toDir + NAME列の値から作成する。
1044                 * 返り値は、new File[] { formFile , toFile }; とする。
1045                 *
1046                 * @og.rev 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定します。
1047                 * @og.rev 7.2.7.0 (2020/08/07) 入力共通パス(inPath)指定時は、TO_PARENTかPARENTの桁数より小さい場合のみ処理する。
1048                 *
1049                 * @param       rowNo   カラムNo
1050                 * @return      ファイル配列(0:オリジナルファイル 1:ターゲットファイル)
1051                 * @og.rtnNotNull
1052                 */
1053                public File[] makeFromToFile( final int rowNo ) {
1054                        final String[] value = table.getValues( rowNo );
1055                        final File fromDir  = mkDirs( value[PARENT],createDir );
1056                        final File formFile = new File( fromDir, value[NAME] );
1057                        File tempToDir = toDir;
1058
1059                        equalParent = false;    // 最後に実行された処理で、親フォルダが同一かどうかのフラグをリセットする。
1060                        if( tempToDir == null ) {
1061                                if( TO_PARENT >= 0 && nval( value[TO_PARENT],null ) != null ) {
1062                                        tempToDir = mkDirs( value[TO_PARENT],createDir );
1063                                }
1064                                else {
1065                                        tempToDir = fromDir;
1066                                        equalParent = true;             // 最後に実行された処理で、親フォルダが同一の場合は、true
1067                                }
1068                        }
1069                        // 6.8.0.0 (2017/06/02) toDirが指定され、かつ、入力共通パスの文字数(inPathCnt)が指定された場合。
1070                        else if( inPathCnt > 0 ) {
1071                                if( TO_PARENT >= 0 && nval( value[TO_PARENT],null ) != null ) {
1072                                        // 7.2.7.0 (2020/08/07) 入力共通パス(inPath)指定時は、TO_PARENTかPARENTの桁数より小さい場合のみ処理する。
1073                                        if( value[TO_PARENT].length() > inPathCnt ) {
1074                                                final String prntVal = toDir.getAbsolutePath() + value[TO_PARENT].substring( inPathCnt );
1075                                                tempToDir = mkDirs( prntVal,createDir );
1076                                        }
1077                                }
1078                                else {
1079                                        // 7.2.7.0 (2020/08/07) 入力共通パス(inPath)指定時は、TO_PARENTかPARENTの桁数より小さい場合のみ処理する。
1080                                        if( value[PARENT].length() > inPathCnt ) {
1081                                                final String prntVal = toDir.getAbsolutePath() + value[PARENT].substring( inPathCnt );
1082                                                tempToDir = mkDirs( prntVal,createDir );
1083                                        }
1084                                }
1085                        }
1086
1087                        File toFile = null;
1088                        if( TO_NAME >= 0 && nval(value[TO_NAME],null) != null  ) {
1089                                toFile = new File( tempToDir, value[TO_NAME] );
1090                        }
1091                        else {
1092                                toFile = new File( tempToDir, value[NAME] );
1093                        }
1094
1095                        return new File[] { formFile , toFile };
1096                }
1097
1098                /**
1099                 * 最後に実行された処理で、親フォルダが同一かどうかを返します(同一の場合は、true)。
1100                 *
1101                 * makeFromToFile( int ) が処理されたときの、FromDir と toDir が同一であれば、true を、
1102                 * 異なる場合は、false を返します。
1103                 * ここでの結果は、厳密な同一判定ではなく、処理的に、同一かどうかを判定しています。
1104                 * つまり、toDir に FromDir をセットする(3.Cのケース)場合に、true を内部変数にセットします。
1105                 * この判定値は、ファイルの移動処理で、異なる親フォルダの場合は、COPY & DELETE しなければ
1106                 * なりませんが、同一親フォルダの場合は、RENAME で済む という処理負荷の軽減が目的です。
1107                 * よって、結果的に、PARENT と TO_PARENT が同じとか、PARENT と targetDir が同じでも
1108                 * ここでのフラグは、false が返されます。
1109                 *
1110                 * @return      最後に実行された処理で、親フォルダが同一の場合は、true
1111                 * @see         #makeFromToFile( int )
1112                 */
1113                public boolean lastParentEquals() {
1114                        return equalParent ;
1115                }
1116
1117                /**
1118                 * 一番最後にmkdirsしたフォルダ名(Exceptionの直前の設定値)を返します。
1119                 *
1120                 * mkdirs でException が発生した場合、発生元のフォルダ名を表示するためのデバッグ用です。
1121                 *
1122                 * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
1123                 *
1124                 * @return      一番最後にmkdirsしたフォルダ名(Exceptionの直前の設定値)
1125                 */
1126                public String getLastDir() {
1127                        return lastDir ;
1128                }
1129
1130        //      /**
1131        //       *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
1132        //       *
1133        //       * ここでは、TO_PARENT や TO_NAME は、存在するかどうか不明なので、
1134        //       * EXCEPTION にせず、配列番号に、-1 を返すようにしています。
1135        //       *
1136        //       * @param       table     一覧が格納されているDBTableModel
1137        //       * @param       nameArray       カラム名配列
1138        //       * @return      カラムNo配列(カラム名が存在しない場合は、-1)
1139        //       */
1140        //      private int[] getTableColumnNo( final DBTableModel table ,final String[] nameArray ) {
1141        //              int[] clmNo = new int[ nameArray.length ];
1142        //              for( int i=0; i<clmNo.length; i++ ) {
1143        //                      clmNo[i] = table.getColumnNo( nameArray[i] , false );   // カラム名が存在しない場合は、-1 を返す。
1144        //              }
1145        //              return clmNo;
1146        //      }
1147
1148                /**
1149                 * フォルダを作成します。
1150                 *
1151                 * フォルダが存在しない場合は、途中階層をすべて作成します。
1152                 *
1153                 * @og.rev 7.0.5.0 (2019/09/13) target のパスも、正規パス名から作成。
1154                 * @og.rev 7.2.7.0 (2020/08/07) 相対パスの場合の基準フォルダ(FILE_URL) 考慮
1155                 *
1156                 * @param       fname   フォルダ名
1157                 * @param       createDir       フォルダ作成可否 [true:作成する/false:作成しない]
1158                 * @return      フォルダを表すファイルオブジェクト。引数が null の場合は、null を返します。
1159                 * @throws      HybsSystemException             ファイルか、存在しない場合に、createDir=false か、mkdirs() が false の場合
1160                 */
1161                private File mkDirs( final String fname , final boolean createDir ) {
1162                        lastDir = fname;                                                // 7.2.7.0 (2020/08/07)
1163                        File target = null;
1164                        if( fname != null ) {
1165                                // 7.0.5.0 (2019/09/13) target のパスも、正規パス名から作成。
1166//                              target = new File( fname );
1167                                try {
1168//                                      target = new File( fname ).getCanonicalFile();
1169                                        final String fname2 = StringUtil.urlAppend( fileURL,fname );                    // 7.2.7.0 (2020/08/07)
1170                                        target = new File( HybsSystem.url2dir( fname2 ) ).getCanonicalFile();   // 7.2.7.0 (2020/08/07)
1171                                        if( target.exists() ) {                 // 存在する
1172                                                if( target.isFile() ) {
1173                                                        final String errMsg = "ターゲットに、ファイル名は指定できません。" + CR
1174                                                                                                + "ターゲット=[" + fname2 + "]"  + CR;
1175                                                        throw new HybsSystemException( errMsg );
1176                                                }
1177                                        }
1178                                        else {                                                  // 存在しない
1179                                                // 存在しないのに、作成しない
1180                                                if( !createDir ) {
1181                                                        final String errMsg = "ターゲットが存在しません。 " + CR
1182                                                                                                + "ターゲット=[" + fname2 + "]"  + CR;
1183                                                        throw new HybsSystemException( errMsg );
1184                                                }
1185                                                // 作成できない
1186                                                if( !target.mkdirs() ) {
1187                                                        final String errMsg = "ターゲットを自動作成使用としましたが、作成できませんでした。" + CR
1188                                                                                                + "ターゲット=[" + fname2 + "]"  + CR;
1189                                                        throw new HybsSystemException( errMsg );
1190                                                }
1191                                        }
1192                                }
1193                                catch( final IOException ex ) {
1194                                        final String errMsg = "File#getCanonicalFile() で、正式パス名を求めることができませんでした。" + CR
1195                                                                                + ex.getMessage()
1196                                                                                + "ターゲット=[" + fname + "]"  + CR;
1197                                        throw new HybsSystemException( errMsg,ex );
1198                                }
1199                        }
1200                        return target;
1201                }
1202        }
1203
1204        /**
1205         * このオブジェクトの文字列表現を返します。
1206         * 基本的にデバッグ目的に使用します。
1207         *
1208         * @return このクラスの文字列表現
1209         * @og.rtnNotNull
1210         */
1211        @Override
1212        public String toString() {
1213                return ToString.title( this.getClass().getName() )
1214                                .println( "VERSION"                     ,VERSION                )
1215                                .println( "action"                      ,action                 )
1216                                .println( "command"                     ,command                )
1217                                .println( "targetDir"           ,targetDir              )
1218                                .println( "createDir"           ,createDir              )
1219                                .println( "tableId"                     ,tableId                )
1220//                              .println( "outMessage"          ,outMessage     )
1221                                .println( "displayMsg"          ,displayMsg     )
1222                                .println( "selectedAll"         ,selectedAll    )
1223                                .println( "keepTimeStamp"       ,keepTimeStamp  )
1224                                .fixForm().toString() ;
1225        }
1226}