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                                        if( !file.delete() ) {                  // 7.3.0.0 (2021/01/06) SpotBugs 例外的戻り値を無視しているメソッド
531                                                System.err.println( "file Delete Error! " + file );
532                                        }
533                                }
534                        }
535                }
536//              inFile.delete();
537                if( !inFile.delete() ) {                                        // 7.3.0.0 (2021/01/06) SpotBugs 例外的戻り値を無視しているメソッド
538                        System.err.println( "inFile Delete Error! " + inFile );
539                }
540        }
541
542        /**
543         * MKDIRS アクションを実行します。
544         *
545         * この処理では、TO_PARENT カラムに指定されたターゲットフォルダを 多階層mkdirします。
546         * 実際は、ファイル作成やコピー時には、mkdirs するので、ここでの使い方の想定は、
547         * ターゲットフォルダへのアクセスが可能かどうかの確認です。
548         * 存在チェック(EXISTS)では、本当に存在しない場合も、アクセス出来ない場合も
549         * false となるためです。
550         * なお、通常の mkdirs では、フォルダが新規に作成されなかった場合(すでに存在)
551         * false となりますが、ここのアクションでは、アクセスチェックも兼ねているため、
552         * mkdirs が成功するか、すでに存在している場合は、正常と判断します。
553         *
554         * エラー発生時の引数は、アクション:{0} From:"" To:{2} です。
555         *
556         * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
557         *
558         * @param       rowNo           処理を実施する行番号
559         * @param       fromToFiles     FromFile,ToFileをまとめた補助クラス
560         * @throws      HybsSystemException     処理中に何らかのエラーが発生した場合
561         */
562        private void actionMKDIRS( final int[] rowNo , final FromToFiles fromToFiles ) {
563                final int rowCount = rowNo.length ;
564                for( int i=0; i<rowCount; i++ ) {
565                        try {
566                                fromToFiles.makeToParent( rowNo[i] );           // 戻り値は確認しない。
567                        }
568                        catch( final Throwable th ) {
569                                if( errMessage == null ) { errMessage = new ErrorMessage( "FileUpdateTag Error" ); }
570                                // ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
571                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,actErrMsg,action,"",fromToFiles.getLastDir() );
572                                errMessage.addMessage( rowNo[i]+1,ErrorMessage.NG,action,th.getMessage() );
573                                errCode = ErrorMessage.NG;
574                        }
575                }
576        }
577
578        /**
579         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。
580         *
581         * @return      選択行の配列
582         * @og.rtnNotNull
583         */
584        @Override
585        protected int[] getParameterRows() {
586                final int[] rowNo ;
587                if( selectedAll ) {
588                        final int rowCnt = table.getRowCount();
589                        rowNo = new int[ rowCnt ];
590                        for( int i=0; i<rowCnt; i++ ) {
591                                rowNo[i] = i;
592                        }
593                } else {
594                        rowNo = super.getParameterRows();
595                }
596                return rowNo ;
597        }
598
599        /**
600         * 【TAG】アクション[COPY|MOVE|MODIFY|DELETE|MKDIRS]をセットします。
601         *
602         * @og.tag
603         * アクションは、ファイルをコピー(COPY)したり、移動(MOVE,MODIFY)したり、削除(DELETE)、
604         * フォルダ作成(MKDIRS)するなどの操作を指定する必須属性です。
605         *
606         * <table class="plain">
607         *   <caption>action属性(必須)のキーワード</caption>
608         *   <tr><th>action</th><th>名称</th><th>機能</th></tr>
609         *   <tr><td>COPY  </td><td>コピー</td><td>オリジナルファイルを、ターゲット(TO_PARENT,TO_NAMEで指定)にコピーします。</td></tr>
610         *   <tr><td>MOVE  </td><td>移動  </td><td>オリジナルファイルを、ターゲットに移動(COPY+DELETE)/名称変更(RENAME)します。</td></tr>
611         *   <tr><td>MODIFY</td><td>移動  </td><td>(MOVE と同じ。エンジンの command を利用するための簡易action)</td></tr>
612         *   <tr><td>DELETE</td><td>削除  </td><td>オリジナルファイルを、削除します。(フォルダ、ファイルに関わらず)</td></tr>
613         *   <tr><td>MKDIRS</td><td>削除  </td><td>ターゲット(TO_PARENTで指定)フォルダを作成します。 7.2.7.0 (2020/08/07)</td></tr>
614         * </table>
615         *
616         * @og.rev 6.3.4.0 (2015/08/01) Arrays.toString から String.join に置き換え。
617         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
618         *
619         * @param       act アクション (public static final 宣言されている文字列)
620         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.FileUpdateTag.ACT_COPY">アクション定数</a>
621         */
622        public void setAction( final String act ) {
623                action = nval( getRequestParameter( act ),action );
624
625                if( action != null && !check( action, ACTION_SET ) ) {
626                        final String errMsg = "指定のアクションは実行できません。アクションエラー"       + CR
627                                                        + "action=[" + action + "] "                                                            + CR
628                                                        + "actionList=" + String.join( ", " , ACTION_SET ) ;
629                        throw new HybsSystemException( errMsg );
630                }
631        }
632
633        /**
634         * 【TAG】ターゲットとなるフォルダを指定します(初期値:null)。
635         *
636         * @og.tag
637         * targetDir 属性を利用する場合は、引数のファイル、またはフォルダが指定されたことに
638         * なります。COPY、MOVE(,MODIFY) の場合は、targetDir 属性にフォルダを指定することで一括処理可能です。
639         * 指定先のフォルダが存在しない場合は、createDir属性の値により処理が異なります。
640         * createDir="true"(初期値)で、ターゲットフォルダが存在しない場合は、自動作成します。
641         *
642         * @param  dir ターゲットとなるフォルダ
643         * @see         #setCreateDir( String )
644         */
645        public void setTargetDir( final String dir ) {
646                targetDir = nval( getRequestParameter( dir ),targetDir );
647        }
648
649        /**
650         * 【TAG】ターゲットとなるフォルダがなければ、作成するかどうかを指定します(初期値:true)。
651         *
652         * @og.tag
653         * COPY,MOVE(,MODIFY) などの処理で、ターゲットフォルダが存在しないときに、作成するか、エラーにするかを
654         * createDir属性 で指定できます。
655         * これは、COPY先やMOVE(,MODIFY)先が存在している前提のシステムで、不要な箇所に間違ってフォルダを
656         * 自動作成されると困る場合に、false:作成しない とすれば、間違いに気づく確率が上がります。
657         * 初期値は true:作成する です。
658         *
659         * @param       flag    フォルダ作成可否 [true:作成する/false:作成しない]
660         */
661        public void setCreateDir( final String flag ) {
662                createDir = nval( getRequestParameter( flag ),createDir );
663        }
664
665        /**
666         * 【TAG】fileQuery以外の場合の元親フォルダを示すカラム名を指定します(初期値:"PARENT")。
667         *
668         * @og.tag
669         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
670         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
671         * それぞれのカラム名を指定できるようにします。
672         * (初期値:"PARENT")
673         *
674         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
675         *
676         * @param       clm 元親フォルダを示すカラム名
677         */
678        public void setParentClm( final String clm ) {
679                parentClm = nval( getRequestParameter( clm ),parentClm );
680        }
681
682        /**
683         * 【TAG】fileQuery以外の場合の元ファイル名を示すカラム名を指定します(初期値:"NAME")。
684         *
685         * @og.tag
686         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
687         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
688         * それぞれのカラム名を指定できるようにします。
689         * (初期値:"NAME")
690         *
691         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
692         *
693         * @param       clm 元ファイル名を示すカラム名
694         */
695        public void setNameClm( final String clm ) {
696                nameClm = nval( getRequestParameter( clm ),nameClm );
697        }
698
699        /**
700         * 【TAG】fileQuery以外の場合の先親フォルダを示すカラム名を指定します(初期値:"TO_PARENT")。
701         *
702         * @og.tag
703         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
704         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
705         * それぞれのカラム名を指定できるようにします。
706         * (初期値:"TO_PARENT")
707         *
708         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
709         *
710         * @param       clm 先親フォルダを示すカラム名
711         */
712        public void setToParentClm( final String clm ) {
713                toParentClm = nval( getRequestParameter( clm ),toParentClm );
714        }
715
716        /**
717         * 【TAG】fileQuery以外の場合の先ファイル名を示すカラム名を指定します(初期値:"TO_NAME")。
718         *
719         * @og.tag
720         * 通常、fileQueryで取得したフォルダとファイル名は、PARENT,NAME というカラムに作成されます。
721         * ここでは、データベース等で管理しているフォルダやファイル名を検索した場合に処理できるように
722         * それぞれのカラム名を指定できるようにします。
723         * (初期値:"TO_NAME")
724         *
725         * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
726         *
727         * @param       clm 先ファイル名を示すカラム名
728         */
729        public void setToNameClm( final String clm ) {
730                toNameClm = nval( getRequestParameter( clm ),toNameClm );
731        }
732
733        /**
734         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
735         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
736         *
737         * @og.tag
738         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
739         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
740         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
741         * この tableId 属性を利用して、メモリ空間を分けます。
742         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
743         *
744         * @param       id テーブルID (sessionに登録する時のID)
745         */
746        public void setTableId( final String id ) {
747                tableId = nval( getRequestParameter( id ),tableId );
748        }
749
750        /**
751         * 【TAG】コマンド (ENTRY)をセットします(初期値:ENTRY)。
752         *
753         * @og.tag
754         * このタグは、command="ENTRY" でのみ実行されます。
755         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
756         * フィールド定数値のいづれかを、指定できます。
757         * 初期値は、ENTRY なので、何も指定しなければ、実行されます。
758         *
759         * @param       cmd コマンド (public static final 宣言されている文字列)
760         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.FileUpdateTag.CMD_ENTRY">コマンド定数</a>
761         */
762        public void setCommand( final String cmd ) {
763                final String cmd2 = getRequestParameter( cmd );
764                if( cmd2 != null && cmd2.length() >= 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
765        }
766
767//      /**
768//       * 【TAG】検索結果のメッセージを表示する/しない[true/false]を指定します(初期値:true)。
769//       *
770//       * @og.tag
771//       * 初期値は、表示する:true です。
772//       *
773//       * @og.rev 7.2.7.0 (2020/08/07) outMessage 削除
774//       *
775//       * @param       flag  メッセージ表示可否 [true:表示する/それ以外:含めない]
776//       */
777//      public void setOutMessage( final String flag ) {
778//              outMessage = nval( getRequestParameter( flag ),outMessage );
779//      }
780
781        /**
782         * 【TAG】処理結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0040[ 件登録しました])。
783         *
784         * @og.tag
785         * ここでは、検索結果の件数や登録された件数をまず出力し、
786         * その次に、ここで指定したメッセージをリソースから取得して表示します。
787         * 表示させたくない場合は, displayMsg = "" をセットしてください。
788         * displayMsg の初期値は、MSG0040[ 件登録しました]です。
789         *
790         * @param       id 処理結果表示メッセージID
791         */
792        public void setDisplayMsg( final String id ) {
793                final String ids = getRequestParameter( id );
794                if( ids != null ) { displayMsg = ids; }
795        }
796
797        /**
798         * 【TAG】action実行時のエラーメッセージのメッセージリソースIDを指定します(初期値:ERR0050:アクション実行中にエラーが発生しました。
799         *
800         * @og.tag
801         * action実行時のエラーメッセージは、COPY|MOVE|MODIFY|DELETE|MKDIRS などのアクション実行時に
802         * エラーが発生した際に、表示するメッセージリソースのID をセットします。
803         * エラーメッセージのため、未指定にすることはできません。
804         * actErrMsg の初期値は、ERR0050:アクション実行中にエラーが発生しました。アクション:{0} From:{1} To:{2}
805         *
806         * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
807         *
808         * @param       id action実行時のエラーメッセージのメッセージリソースID
809         */
810        public void setActErrMsg( final String id ) {
811                actErrMsg = nval( getRequestParameter( id ),actErrMsg );
812        }
813
814        /**
815         * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
816         *
817         * @og.tag
818         * 全てのデータを選択済みデータとして扱って処理します。
819         * 全件処理する場合に、(true/false)を指定します。
820         * 初期値は false です。
821         *
822         * @param  all 全件選択済み指定 [true:全件選択済み/false:通常]
823         */
824        public void setSelectedAll( final String all ) {
825                selectedAll = nval( getRequestParameter( all ),selectedAll );
826        }
827
828        /**
829         * 【TAG】オリジナルのタイムスタンプを利用するかどうかを指定します(初期値:false)。
830         *
831         * @og.tag
832         * COPYや親違いMOVE(,MODIFY)の時に、オリジナルのタイムスタンプをそのままコピー先のファイルにも
833         * 適用するかどうかを指定します。
834         * タイムスタンプを初期化されたくない場合に、true に設定します。
835         * 初期値は 利用しない:false です。
836         *
837         * @param  flag タイムスタンプ利用 [true:する/false:しない]
838         */
839        public void setKeepTimeStamp( final String flag ) {
840                keepTimeStamp = nval( getRequestParameter( flag ),keepTimeStamp );
841        }
842
843        /**
844         * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します
845         *              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
846         *
847         * @og.tag
848         * true に設定すると、処理時間を表示するバーイメージが表示されます。
849         * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで
850         * 表示させる機能です。処理時間の目安になります。
851         * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
852         *
853         * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。
854         *
855         * @param       flag    処理時間を表示 [true:する/false:しない]
856         */
857        public void setUseTimeView( final String flag ) {
858                useTimeView = nval( getRequestParameter( flag ),useTimeView );
859        }
860
861        /**
862         * 【TAG】入力共通パスを指定します。
863         *
864         * @og.tag
865         * 通常、fileQueryタグ等で、検索した結果は、PARENT,NAME というカラムにセットされます。
866         * この、fileQueryのfrom属性に、検索を開始するディレクトリを指定し、multi="true"で、
867         * 多段階展開 した場合、この、from属性に指定したパスを、inPath にセットすることで、
868         * 以下の階層フォルダそのままに、targetDir にCOPY または、MOVE することが出来ます。
869         * 逆に、指定しない場合は、フォルダ階層無しで、COPY,MOVE されます。
870         *
871         * @og.rev 6.8.0.0 (2017/06/02) 入力共通パスを指定します。
872         * @og.rev 7.0.5.0 (2019/09/13) inPath のパスは、正規パス名から作成。
873         *
874         * @param       path    入力共通パス
875         */
876        public void setInPath( final String path ) {
877                inPath = nval( getRequestParameter( path ),inPath );
878
879                // 7.0.5.0 (2019/09/13) inPath のパスは、正規パス名から作成。
880                if( inPath != null ) {
881                        try {
882                                inPath = new File(inPath).getCanonicalPath();
883                        }
884                        catch( final IOException ex ) {
885                                final String errMsg = "inPathの正式なファイル名の取得に失敗しました。[" + inPath + "]"
886                                                        + CR + ex.getMessage();
887                                throw new HybsSystemException( errMsg,ex );
888                        }
889                }
890        }
891
892        /**
893         * 【TAG】エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
894         *
895         * @og.tag
896         * 通常のエラーメッセージは、ラベル(長)が使われますが、これをラベル(短)を使いたい場合に、true にセットします。
897         * ここでのラベル(短)は、タグ修飾なしの、ラベル(短)です。
898         * 標準はfalse:利用しない=ラベル(長)です。
899         * true/false以外を指定した場合はfalse扱いとします。
900         *
901         * ラベルリソースの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
902         *
903         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
904         *
905         * @param prm SLABEL利用 [true:利用する/false:利用しない]
906         */
907        public void setUseSLabel( final String prm ) {
908                useSLabel = nval( getRequestParameter( prm ),useSLabel );
909        }
910
911        /**
912         * 【TAG】エラー時に処理後に停止するかどうか[true/false]を指定します(初期値:true)。
913         *
914         * @og.tag
915         * 処理結果などに応じて、以下の処理を停止したい場合に、使用します。
916         * false を指定すると、判定結果に無関係に、以下の処理を実行します。
917         * 処理は継続したいが、警告表示する場合に、useStop="false" を指定します。
918         * 初期値は、停止する ("true")です。
919         *
920         * @og.rev 7.2.7.0 (2020/08/07) エラー時に処理後に停止(true)するかどうか(useStop対応)
921         *
922         * @param   flag 処理後停止 [true:する/それ以外:しない]
923         */
924        public void setUseStop( final String flag ) {
925                useStop = nval( getRequestParameter( flag ),useStop );
926        }
927
928        /**
929         * DBTableModel から、FromFile,ToFile を作成するための処理をまとめた補助クラスです。
930         *
931         * ここでは、オリジナルファイルやターゲットファイルを作成するための処理のみを集めています。
932         * メソッドにすると、ローカル変数を多く管理するか、多数の引数渡しを繰り返すことになるため、
933         * このローカルクラスに処理と、値を格納します。
934         *
935         * @og.rev 7.2.7.0 (2020/08/07) 相対パスの場合の基準フォルダ(FILE_URL) 考慮
936         */
937        private static final class FromToFiles {
938                private final String fileURL = HybsSystem.sys( "FILE_URL" );            // 7.2.7.0 (2020/08/07) 相対パスの場合の基準フォルダ
939
940                private final DBTableModel      table ;
941
942                private final int PARENT        ;
943                private final int NAME          ;
944                private final int TO_PARENT     ;
945                private final int TO_NAME       ;
946
947                private final File              toDir   ;
948                private final boolean   createDir;              // ターゲットとなるフォルダがなければ、作成するかどうか(true:作成する)
949                private final int               inPathCnt;              // 6.8.0.0 (2017/06/02) 入力共通パスの文字数
950
951                private boolean equalParent     ;                       // 最後に実行された処理で、親フォルダが同一の場合は、true
952                private String  lastDir ;                               // 7.2.7.0 (2020/08/07) 一番最後にmkdirsしたフォルダ名(Exceptionの直前の設定値)
953
954                /**
955                 *  引数指定のコンストラクター
956                 *
957                 * 必要なパラメータを渡して、オブジェクトを構築します。
958                 * 入力共通パス(inPath)は、COPY/MOVE時にtargetDirと置換されます。
959                 *
960                 * @og.rev 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定します。
961                 * @og.rev 7.2.7.0 (2020/08/07) parentClm,nameClm,toParentClm,toNameClm 追加
962                 *
963                 * @param       table     一覧が格納されているDBTableModel
964                 * @param       targetDir       ターゲットとなるフォルダ
965                 * @param       createDir       フォルダ作成可否 [true:作成する/false:作成しない]
966                 * @param       inPath          入力共通パス
967                 * @param       clms            parentClm,nameClm,toParentClm,toNameClm の配列
968                 */
969//              public FromToFiles( final DBTableModel table , final String targetDir , final boolean createDir , final String inPath ) {
970                public FromToFiles( final DBTableModel table , final String targetDir , final boolean createDir , final String inPath , final String[] clms ) {
971
972                        this.table              = table;
973                        this.createDir  = createDir ;
974                        toDir                   = mkDirs( targetDir,createDir );                        // targetDir が指定されていない場合は、null
975                        // 7.0.5.0 (2019/09/13) inPath のパスの文字数も、正規パス名から作成。
976                        inPathCnt               = inPath == null ? 0 : inPath.length() ;        // 6.8.0.0 (2017/06/02) 入力共通パスの文字数
977
978                        // "PARENT","NAME","TO_PARENT","TO_NAME" のカラム名のDBTableModelのカラム番号。存在しない場合は、-1
979//                      PARENT          = table.getColumnNo( "PARENT"   , false );
980//                      NAME            = table.getColumnNo( "NAME"             , false );
981//                      TO_PARENT       = table.getColumnNo( "TO_PARENT", false );
982//                      TO_NAME         = table.getColumnNo( "TO_NAME"  , false );
983
984                        PARENT          = table.getColumnNo( clms[0]    , false );              // 7.2.7.0 (2020/08/07) parentClm
985                        NAME            = table.getColumnNo( clms[1]    , false );              // 7.2.7.0 (2020/08/07) nameClm
986                        TO_PARENT       = table.getColumnNo( clms[2]    , false );              // 7.2.7.0 (2020/08/07) toParentClm
987                        TO_NAME         = table.getColumnNo( clms[3]    , false );              // 7.2.7.0 (2020/08/07) toNameClm
988                }
989
990                /**
991                 * 行番号より、対応するオリジナルファイル(FromFile)を返します。
992                 *
993                 * ここでは、TO_PARENT や TO_NAME は、判定する必要がないため、makeFromToFile( int ) の
994                 * 一部のみで処理が終了できます。
995                 * 1.FromDir は、PARENT 列の値から作成する。
996                 * 2.FromFileは、FromDir + NAME列の値から作成する。
997                 *
998                 * 配列返しの関係で、メソッド(および内部処理)を分けています。
999                 *
1000                 * @param       rowNo   カラムNo
1001                 * @return      オリジナルファイル(FromFile)
1002                 * @og.rtnNotNull
1003                 * @see         #makeFromToFile( int )
1004                 */
1005                public File makeFromOnly( final int rowNo ) {
1006                        final String[] value = table.getValues( rowNo );
1007                        final File fromDir  = mkDirs( value[PARENT],createDir );
1008
1009                        return new File( fromDir, value[NAME] ) ;
1010                }
1011
1012                /**
1013                 * 行番号より、対応するTO_PARENT フォルダのFileオブジェクトを返します。
1014                 *
1015                 * ここでは、TO_PARENT のみで処理します。
1016                 *
1017                 * 実質的には、MKDIRSアクション でしか使用しないので、Fileオブジェクトを作成する必要すら
1018                 * ありませんが、メソッド共通化の関係で、オリジナルをそのまま使用します。
1019                 *
1020                 * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
1021                 *
1022                 * @param       rowNo   カラムNo
1023                 * @return      TO_PARENT フォルダのFileオブジェクト(カラムが存在しない場合は、null)
1024                 */
1025                public File makeToParent( final int rowNo ) {
1026                        final File rtnFile ;
1027                        if( TO_PARENT >= 0 ) {
1028                                final String[] value = table.getValues( rowNo );
1029                                rtnFile = mkDirs( value[TO_PARENT],true );
1030                        }
1031                        else {
1032                                rtnFile = null;
1033                        }
1034                        return rtnFile;
1035                }
1036
1037                /**
1038                 * 行番号より、対応するオリジナルファイル(FromFile)とターゲットファイル(ToFile)を配列に格納して返します。
1039                 *
1040                 * ここでは、TO_PARENT や TO_NAME は、存在するかどうか不明なので、以下の手順で作成します。
1041                 * 1.FromDir は、PARENT 列の値から作成する。
1042                 * 2.FromFileは、FromDir + NAME列の値から作成する。
1043                 * 3.toDir は、
1044                 *       A.targetDir が有れば、それを使う。
1045                 *       B.なければ、TO_PARENT 列の値から作成する。
1046                 *       C.TO_PARENT 列がないか、値が未設定の場合は、FromDir をそのまま使う。
1047                 * 4.toFile は、
1048                 *       A.toDir + TO_NAME 列の値から作成する。
1049                 *       B.TO_NAME 列がないか、値が未設定の場合は、toDir + NAME列の値から作成する。
1050                 * 返り値は、new File[] { formFile , toFile }; とする。
1051                 *
1052                 * @og.rev 6.8.0.0 (2017/06/02) 入力共通パス(inPath)を指定します。
1053                 * @og.rev 7.2.7.0 (2020/08/07) 入力共通パス(inPath)指定時は、TO_PARENTかPARENTの桁数より小さい場合のみ処理する。
1054                 *
1055                 * @param       rowNo   カラムNo
1056                 * @return      ファイル配列(0:オリジナルファイル 1:ターゲットファイル)
1057                 * @og.rtnNotNull
1058                 */
1059                public File[] makeFromToFile( final int rowNo ) {
1060                        final String[] value = table.getValues( rowNo );
1061                        final File fromDir  = mkDirs( value[PARENT],createDir );
1062                        final File formFile = new File( fromDir, value[NAME] );
1063                        File tempToDir = toDir;
1064
1065                        equalParent = false;    // 最後に実行された処理で、親フォルダが同一かどうかのフラグをリセットする。
1066                        if( tempToDir == null ) {
1067                                if( TO_PARENT >= 0 && nval( value[TO_PARENT],null ) != null ) {
1068                                        tempToDir = mkDirs( value[TO_PARENT],createDir );
1069                                }
1070                                else {
1071                                        tempToDir = fromDir;
1072                                        equalParent = true;             // 最後に実行された処理で、親フォルダが同一の場合は、true
1073                                }
1074                        }
1075                        // 6.8.0.0 (2017/06/02) toDirが指定され、かつ、入力共通パスの文字数(inPathCnt)が指定された場合。
1076                        else if( inPathCnt > 0 ) {
1077                                if( TO_PARENT >= 0 && nval( value[TO_PARENT],null ) != null ) {
1078                                        // 7.2.7.0 (2020/08/07) 入力共通パス(inPath)指定時は、TO_PARENTかPARENTの桁数より小さい場合のみ処理する。
1079                                        if( value[TO_PARENT].length() > inPathCnt ) {
1080                                                final String prntVal = toDir.getAbsolutePath() + value[TO_PARENT].substring( inPathCnt );
1081                                                tempToDir = mkDirs( prntVal,createDir );
1082                                        }
1083                                }
1084                                else {
1085                                        // 7.2.7.0 (2020/08/07) 入力共通パス(inPath)指定時は、TO_PARENTかPARENTの桁数より小さい場合のみ処理する。
1086                                        if( value[PARENT].length() > inPathCnt ) {
1087                                                final String prntVal = toDir.getAbsolutePath() + value[PARENT].substring( inPathCnt );
1088                                                tempToDir = mkDirs( prntVal,createDir );
1089                                        }
1090                                }
1091                        }
1092
1093                        File toFile = null;
1094                        if( TO_NAME >= 0 && nval(value[TO_NAME],null) != null  ) {
1095                                toFile = new File( tempToDir, value[TO_NAME] );
1096                        }
1097                        else {
1098                                toFile = new File( tempToDir, value[NAME] );
1099                        }
1100
1101                        return new File[] { formFile , toFile };
1102                }
1103
1104                /**
1105                 * 最後に実行された処理で、親フォルダが同一かどうかを返します(同一の場合は、true)。
1106                 *
1107                 * makeFromToFile( int ) が処理されたときの、FromDir と toDir が同一であれば、true を、
1108                 * 異なる場合は、false を返します。
1109                 * ここでの結果は、厳密な同一判定ではなく、処理的に、同一かどうかを判定しています。
1110                 * つまり、toDir に FromDir をセットする(3.Cのケース)場合に、true を内部変数にセットします。
1111                 * この判定値は、ファイルの移動処理で、異なる親フォルダの場合は、COPY & DELETE しなければ
1112                 * なりませんが、同一親フォルダの場合は、RENAME で済む という処理負荷の軽減が目的です。
1113                 * よって、結果的に、PARENT と TO_PARENT が同じとか、PARENT と targetDir が同じでも
1114                 * ここでのフラグは、false が返されます。
1115                 *
1116                 * @return      最後に実行された処理で、親フォルダが同一の場合は、true
1117                 * @see         #makeFromToFile( int )
1118                 */
1119                public boolean lastParentEquals() {
1120                        return equalParent ;
1121                }
1122
1123                /**
1124                 * 一番最後にmkdirsしたフォルダ名(Exceptionの直前の設定値)を返します。
1125                 *
1126                 * mkdirs でException が発生した場合、発生元のフォルダ名を表示するためのデバッグ用です。
1127                 *
1128                 * @og.rev 7.2.7.0 (2020/08/07) MKDIRSアクション 追加
1129                 *
1130                 * @return      一番最後にmkdirsしたフォルダ名(Exceptionの直前の設定値)
1131                 */
1132                public String getLastDir() {
1133                        return lastDir ;
1134                }
1135
1136        //      /**
1137        //       *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
1138        //       *
1139        //       * ここでは、TO_PARENT や TO_NAME は、存在するかどうか不明なので、
1140        //       * EXCEPTION にせず、配列番号に、-1 を返すようにしています。
1141        //       *
1142        //       * @param       table     一覧が格納されているDBTableModel
1143        //       * @param       nameArray       カラム名配列
1144        //       * @return      カラムNo配列(カラム名が存在しない場合は、-1)
1145        //       */
1146        //      private int[] getTableColumnNo( final DBTableModel table ,final String[] nameArray ) {
1147        //              int[] clmNo = new int[ nameArray.length ];
1148        //              for( int i=0; i<clmNo.length; i++ ) {
1149        //                      clmNo[i] = table.getColumnNo( nameArray[i] , false );   // カラム名が存在しない場合は、-1 を返す。
1150        //              }
1151        //              return clmNo;
1152        //      }
1153
1154                /**
1155                 * フォルダを作成します。
1156                 *
1157                 * フォルダが存在しない場合は、途中階層をすべて作成します。
1158                 *
1159                 * @og.rev 7.0.5.0 (2019/09/13) target のパスも、正規パス名から作成。
1160                 * @og.rev 7.2.7.0 (2020/08/07) 相対パスの場合の基準フォルダ(FILE_URL) 考慮
1161                 *
1162                 * @param       fname   フォルダ名
1163                 * @param       createDir       フォルダ作成可否 [true:作成する/false:作成しない]
1164                 * @return      フォルダを表すファイルオブジェクト。引数が null の場合は、null を返します。
1165                 * @throws      HybsSystemException             ファイルか、存在しない場合に、createDir=false か、mkdirs() が false の場合
1166                 */
1167                private File mkDirs( final String fname , final boolean createDir ) {
1168                        lastDir = fname;                                                // 7.2.7.0 (2020/08/07)
1169                        File target = null;
1170                        if( fname != null ) {
1171                                // 7.0.5.0 (2019/09/13) target のパスも、正規パス名から作成。
1172//                              target = new File( fname );
1173                                try {
1174//                                      target = new File( fname ).getCanonicalFile();
1175                                        final String fname2 = StringUtil.urlAppend( fileURL,fname );                    // 7.2.7.0 (2020/08/07)
1176                                        target = new File( HybsSystem.url2dir( fname2 ) ).getCanonicalFile();   // 7.2.7.0 (2020/08/07)
1177                                        if( target.exists() ) {                 // 存在する
1178                                                if( target.isFile() ) {
1179                                                        final String errMsg = "ターゲットに、ファイル名は指定できません。" + CR
1180                                                                                                + "ターゲット=[" + fname2 + "]"  + CR;
1181                                                        throw new HybsSystemException( errMsg );
1182                                                }
1183                                        }
1184                                        else {                                                  // 存在しない
1185                                                // 存在しないのに、作成しない
1186                                                if( !createDir ) {
1187                                                        final String errMsg = "ターゲットが存在しません。 " + CR
1188                                                                                                + "ターゲット=[" + fname2 + "]"  + CR;
1189                                                        throw new HybsSystemException( errMsg );
1190                                                }
1191                                                // 作成できない
1192                                                if( !target.mkdirs() ) {
1193                                                        final String errMsg = "ターゲットを自動作成使用としましたが、作成できませんでした。" + CR
1194                                                                                                + "ターゲット=[" + fname2 + "]"  + CR;
1195                                                        throw new HybsSystemException( errMsg );
1196                                                }
1197                                        }
1198                                }
1199                                catch( final IOException ex ) {
1200                                        final String errMsg = "File#getCanonicalFile() で、正式パス名を求めることができませんでした。" + CR
1201                                                                                + ex.getMessage()
1202                                                                                + "ターゲット=[" + fname + "]"  + CR;
1203                                        throw new HybsSystemException( errMsg,ex );
1204                                }
1205                        }
1206                        return target;
1207                }
1208        }
1209
1210        /**
1211         * このオブジェクトの文字列表現を返します。
1212         * 基本的にデバッグ目的に使用します。
1213         *
1214         * @return このクラスの文字列表現
1215         * @og.rtnNotNull
1216         */
1217        @Override
1218        public String toString() {
1219                return ToString.title( this.getClass().getName() )
1220                                .println( "VERSION"                     ,VERSION                )
1221                                .println( "action"                      ,action                 )
1222                                .println( "command"                     ,command                )
1223                                .println( "targetDir"           ,targetDir              )
1224                                .println( "createDir"           ,createDir              )
1225                                .println( "tableId"                     ,tableId                )
1226//                              .println( "outMessage"          ,outMessage     )
1227                                .println( "displayMsg"          ,displayMsg     )
1228                                .println( "selectedAll"         ,selectedAll    )
1229                                .println( "keepTimeStamp"       ,keepTimeStamp  )
1230                                .fixForm().toString() ;
1231        }
1232}