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