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