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