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     */
016    package org.opengion.fukurou.process;
017    
018    import org.opengion.fukurou.util.Argument;
019    import org.opengion.fukurou.util.FileUtil;
020    import org.opengion.fukurou.util.Closer ;
021    import org.opengion.fukurou.util.LogWriter;
022    
023    import java.util.Map ;
024    import java.util.LinkedHashMap ;
025    
026    import java.io.File;
027    import java.io.PrintWriter;
028    import java.io.BufferedReader;
029    import java.io.IOException;
030    
031    /**
032     * Process_FileCopy は、上流から受け取っã�?FileLineModel をå?ç�?�™るã?
033     * ChainProcess インターフェースの実è£?‚¯ラスですã?
034     *
035     * 上流から受け取っã�?FileLineModel の ファイルから、inPath の共通パス
036     * 以下ã?ファイルをã?outPath の共通パス以下にコピã?しますã?
037     * コピã?の種類ã?、バイナリかã?ãƒ?‚­ストで、テキストã?場合ã?、エンコーãƒ?
038     * 変換も行うことが可能ですã?
039     * inPath と outPath が同じã?またã?、outPath が未設定ã?場合ã?、å?力と出力が
040     * 同じですã?で、è?åˆ??身のエンコード変換処ç�?‚’行うことになりますã?
041     *
042     * コピã?されるファイルのファイル名ã?、å?力ファイル名と同ä¸?�§すã?保存される
043     * フォルãƒ?�Œ異なりますã?(同ä¸?�«することも可能ですã?)
044     *
045     * 上流ã?ロセスでは、Name 属æ?として、ã?File』を持ち、å?は、Fileオブジェクãƒ?
046     * である、Process_FileSearch を使用するのがã?便利ですã?それ以外ã?クラスã‚?
047     * 使用する場合でもã?Name属æ?と、File オブジェクトを持つ LineModel を受け渡ã�?
048     * できれば、使用可能ですã?
049     *
050     * 引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション("") で括って下さã�??
051     * 引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に
052     * 繋げてくださいã€?
053     *
054     * @og.formSample
055     *  Process_FileCopy -inPath=入力å?通パス -inEncode=Windows-31J -outPath=出力å?通パス -outEncode=UTF-8
056     *
057     *    -inPath=入力å?通パス         ?š上流で検索されたファイルパスの共通部åˆ?
058     *   [-inEncode=入力エンコーãƒ?  ] ?šå?力ファイルのエンコードタイãƒ?
059     *   [-outPath=出力å?通パス      ] ?šå?力するファイルパスの共通部åˆ?
060     *   [-outEncode=出力エンコーãƒ? ] ?šå?力ファイルのエンコードタイãƒ?
061     *   [-binary=[false/true]       ] ?štrueは、バイナリファイルのコピã?(初期値:false)
062     *   [-changeCrLf=[false/true]   ] ?štrueは、バイナリファイルのコピã?時にCR+LFに変換しまã�?初期値:false)
063     *   [-keepTimeStamp=[false/true]] ?štrueは、コピã?å…??ファイルのタイãƒ?‚¹タンプで作æ?しまã�?初期値:false)
064     *   [-display=[false/true]      ] ?štrueは、コピã?状況を表示しまã�?初期値:false)
065     *
066     * @version  4.0
067     * @author   Kazuhiko Hasegawa
068     * @since    JDK5.0,
069     */
070    public class Process_FileCopy extends AbstractProcess implements ChainProcess {
071            private File    tempFile        = null;
072    
073            private String  inPath                  = null;
074            private String  inEncode                = null;
075            private String  outPath                 = null;
076            private String  outEncode               = null;
077            private boolean binary                  = false;
078            private boolean changeCrLf              = false;        // 4.2.2.0 (2008/05/10)
079            private boolean keepTimeStamp   = false;        // 5.1.5.0 (2010/04/01)
080            private boolean display                 = false;
081    
082            private int             inPathLen       = 0;
083            private boolean isEquals        = false;
084            private int             inCount         = 0;
085    
086            private static final Map<String,String> mustProparty   ;          // ?»プロパティ?½å¿??チェãƒ?‚¯用 Map
087            private static final Map<String,String> usableProparty ;          // ?»プロパティ?½整合æ?チェãƒ?‚¯ Map
088    
089            static {
090                    mustProparty = new LinkedHashMap<String,String>();
091                    mustProparty.put( "inPath",     "コピã?å…??ファイル基準パス" );
092    
093                    usableProparty = new LinkedHashMap<String,String>();
094                    usableProparty.put( "inEncode",                 "コピã?å…??ファイルのエンコードタイãƒ? );
095                    usableProparty.put( "outPath",                  "コピã?先ã?ファイル基準パス" );
096                    usableProparty.put( "outEncode",                "コピã?先ã?ファイルのエンコードタイãƒ? );
097                    usableProparty.put( "binary",                   "trueは、バイナリファイルをコピã?しまã�?初期値:false)" );
098                    usableProparty.put( "changeCrLf",               "trueは、バイナリファイルのコピã?時にCR+LFに変換しまã�?初期値:false)" );         // 4.2.2.0 (2008/05/10)
099                    usableProparty.put( "keepTimeStamp",    "trueは、コピã?å…??ファイルのタイãƒ?‚¹タンプで作æ?しまã�?初期値:false)" ); // 5.1.5.0 (2010/04/01)
100                    usableProparty.put( "display",                  "trueは、コピã?状況を表示しまã�?初期値:false)" );
101            }
102    
103            /**
104             * ãƒ?ƒ•ォルトコンストラクターã€?
105             * こã?クラスは、動çš?½œæ?されますã?ãƒ?ƒ•ォルトコンストラクターでã€?
106             * super クラスに対して、å¿?¦�な初期化を行っておきますã?
107             *
108             */
109            public Process_FileCopy() {
110                    super( "org.opengion.fukurou.process.Process_FileCopy",mustProparty,usableProparty );
111            }
112    
113            /**
114             * プロセスの初期化を行いますã?初めにä¸?º¦ã�?�‘、呼び出されますã?
115             * 初期処ç�?ファイルオープンã€?¼¤?¢オープンç­?に使用しますã?
116             *
117             * @og.rev 4.2.2.0 (2008/05/10) changeCrLf 属æ?対å¿?
118             * @og.rev 5.1.5.0 (2010/04/01) keepTimeStamp 属æ?の追åŠ?
119             *
120             * @param   paramProcess ãƒ??タベã?スの接続å?æƒ??などを持ってã�?‚‹オブジェクãƒ?
121             */
122            public void init( final ParamProcess paramProcess ) {
123                    Argument arg = getArgument();
124    
125                    inPath                  = arg.getProparty("inPath" );
126                    outPath                 = arg.getProparty("outPath" );
127                    inEncode                = arg.getProparty("inEncode" ,System.getProperty("file.encoding"));
128                    outEncode               = arg.getProparty("outEncode",System.getProperty("file.encoding"));
129                    binary                  = arg.getProparty("binary" ,binary);
130                    changeCrLf              = arg.getProparty("changeCrLf" ,changeCrLf);            // 4.2.2.0 (2008/05/10)
131                    keepTimeStamp   = arg.getProparty("keepTimeStamp" ,keepTimeStamp);      // 5.1.5.0 (2010/04/01)
132                    display                 = arg.getProparty("display",display);
133    
134                    inPathLen = inPath.length();
135    
136                    // 入力と出力が同じかï¼?
137                    isEquals  = outPath == null || inPath.equalsIgnoreCase( outPath );
138    
139                    if( binary ) {
140                            // 4.2.2.0 (2008/05/10) 判定ミスの修正
141                            if( ! inEncode.equalsIgnoreCase( outEncode ) ) {
142                                    String errMsg = "バイナリコピã?時には、å?出力ã?エンコードã?同じå¿?¦�がありますã?" + CR
143                                                            + " inEncode=[" + inEncode + "] , outEncode=[" + outEncode + "]" ;
144                                    throw new RuntimeException( errMsg );
145                            }
146                            if( isEquals ) {
147                                    String errMsg = "入出力が同じファイルのバイナリコピã?はできませんã€? + CR
148                                                            + " inPath=[" + inPath + "] , outPath=[" + outPath + "]" ;
149                                    throw new RuntimeException( errMsg );
150                            }
151                    }
152    
153                    // 入力と出力が同じ場合ã?、中間ファイルを作æ?しますã?
154                    if( isEquals ) {
155                            try {
156                                    tempFile = File.createTempFile( "X", ".tmp", new File( outPath ) );
157                                    tempFile.deleteOnExit();
158                            }
159                            catch( IOException ex ) {
160                                    String errMsg = "中間ファイル作æ?でエラーが発生しましたã€? + CR
161                                                            + " outPath=[" + outPath + "]" ;
162                                    throw new RuntimeException( errMsg,ex );
163                            }
164                    }
165            }
166    
167            /**
168             * プロセスの終äº?‚’行いますã?æœ?¾Œにä¸?º¦ã�?�‘、呼び出されますã?
169             * 終äº??ç�?ファイルクローズã€?¼¤?¢クローズç­?に使用しますã?
170             *
171             * @param   isOK トã?タルで、OKã�?�£たかどã�?�‹[true:成功/false:失敗]
172             */
173            public void end( final boolean isOK ) {
174                    tempFile  = null;
175            }
176    
177            /**
178             * 引数の LineModel をå?ç�?�™るメソãƒ?ƒ‰ですã?
179             * 変換処ç�?¾Œã? LineModel を返しますã?
180             * 後続å?ç�?‚’行わなã�??å�?ãƒ??タのフィルタリングを行う場å�?はã€?
181             * null ãƒ??タを返しますã?つまりã?null ãƒ??タは、後続å?ç�?‚’行わなã�?
182             * フラグの代わりにも使用してã�?�¾すã?
183             * なおã?変換処ç�?¾Œã? LineModel と、オリジナルの LineModel がã?
184             * 同ä¸?�‹、コピã?(クローン)かã?、各処ç�?ƒ¡ソãƒ?ƒ‰å†?�§決めてã�?�¾すã?
185             * ドキュメントに明記されてã�?�ªã�??合ã?、副作用が問題になるå?合ã?ã€?
186             * å�??ç�?�”とに自åˆ?�§コピã?(クローン)して下さã�??
187             *
188             * @og.rev 4.0.0.0 (2007/11/28) メソãƒ?ƒ‰の戻りå?をチェãƒ?‚¯しますã?
189             * @og.rev 4.2.2.0 (2008/05/10) changeCrLf 属æ?対å¿?
190             * @og.rev 4.2.3.0 (2008/05/26) LineModel ã�?FileLineModel でなã�??合ã?処ç�?
191             * @og.rev 5.1.5.0 (2010/04/01) keepTimeStamp 属æ?の追åŠ?
192             * @og.rev 5.1.6.0 (2010/05/01) changeCrLf 属æ?がã?.FileUtil#changeCrLfcopy メソãƒ?ƒ‰への移動に伴ã�?¯¾å¿?
193             *
194             * @param       data    オリジナルのLineModel
195             *
196             * @return      処ç�?¤‰換後ã?LineModel
197             */
198            public LineModel action( final LineModel data ) {
199                    inCount++ ;
200                    final FileLineModel fileData ;
201                    if( data instanceof FileLineModel ) {
202                            fileData = (FileLineModel)data ;
203                    }
204                    else {
205                            // LineModel ã�?FileLineModel でなã�??合ã?オブジェクトを作æ?しますã?
206                            fileData = new FileLineModel( data );
207    //                      String errMsg = "ãƒ??タã�?FileLineModel オブジェクトではありませんã€? + CR ;
208    //                      throw new RuntimeException( errMsg );
209                    }
210    
211                    File inFile = fileData.getFile() ;
212                    if( ! inFile.isFile() ) {
213                            if( display ) { println( data.dataLine() ); }           // 5.1.2.0 (2010/01/01) display の条件変更
214                            return data;
215                    }
216    
217                    // ファイル名を作æ?しますã?
218                    // ファイル名ã?、引数ファイルå�?から、inPath を引き、outPath を加えますã?
219                    File outFile = new File( outPath, inFile.getAbsolutePath().substring( inPathLen ) );
220                    fileData.setFile( outFile );
221    
222    //              if( display ) { println( inFile + " => " + outFile ); }
223    
224                    // 入出力が異なるå?å�?
225                    if( !isEquals ) {
226                            tempFile = outFile;
227                            File parent = outFile.getParentFile();
228                            if( parent != null && ! parent.exists() && !parent.mkdirs() ) {
229                                    String errMsg = "æ‰?®šã?フォルãƒ?�Œ作æ?できませんでしたã€?" + parent + "]" ;
230                                    throw new RuntimeException( errMsg );
231                            }
232                    }
233    
234                    if( binary ) {
235    //                      FileUtil.copy( inFile,tempFile );
236    //                      FileUtil.copy( inFile,tempFile,changeCrLf );    // 4.2.2.0 (2008/05/10)
237                            // 5.1.6.0 (2010/05/01) changeCrLfcopy 対å¿?
238                            if( changeCrLf ) { FileUtil.changeCrLfcopy( inFile,tempFile ); }
239                            else             { FileUtil.copy( inFile,tempFile,keepTimeStamp ); }
240                    }
241                    else {
242                            BufferedReader reader = FileUtil.getBufferedReader( inFile ,inEncode  );
243                            PrintWriter    writer = FileUtil.getPrintWriter( tempFile  ,outEncode );
244    
245                            try {
246                                    String line1;
247                                    while((line1 = reader.readLine()) != null) {
248                                            writer.println( line1 );
249                                    }
250                            }
251                            catch( IOException ex ) {
252                                    String errMsg = "ファイルコピã?中に例外が発生しましたã€?" + data.getRowNo() + "]件目" + CR
253                                                            + " inFile=[" + inFile + "] , tempFile=[" + tempFile + "]" ;
254                                    throw new RuntimeException( errMsg,ex );
255                            }
256                            finally {
257                                    Closer.ioClose( reader ) ;
258                                    Closer.ioClose( writer ) ;
259                            }
260                    }
261    
262                    if( isEquals ) {
263                            if( !outFile.delete() ) {
264                                    String errMsg = "æ‰?®šã?ファイルを削除できませんでしたã€?" + outFile + "]" ;
265                                    throw new RuntimeException( errMsg );
266                            }
267    
268                            if( !tempFile.renameTo( outFile ) ) {
269                                    String errMsg = "æ‰?®šã?ファイルをリネã?ãƒ?�§きませんでしたã€?" + tempFile + "]" ;
270                                    throw new RuntimeException( errMsg );
271                            }
272                    }
273    
274                    // 5.1.5.0 (2010/04/01) keepTimeStamp 属æ?の追åŠ?
275                    if( keepTimeStamp ) {
276                            if( !outFile.setLastModified( inFile.lastModified() ) ) {
277                                    String errMsg = "lastModified 時間の設定が、できませんでしたã€?" + outFile + "]" ;
278                                    throw new RuntimeException( errMsg );
279                            }
280                    }
281    
282                    if( display ) { println( data.dataLine() ); }           // 5.1.2.0 (2010/01/01) display の条件変更
283                    return data ;
284            }
285    
286            /**
287             * プロセスの処ç�?µ�果のレポã?ト表現を返しますã?
288             * 処ç�??ログラãƒ?��、å?力件数、å?力件数などのæƒ??ですã?
289             * こã?æ–?­—å?をそのまま、標準å?力に出すことで、結果レポã?トと出来るよã�?�ª
290             * 形式で出してくださいã€?
291             *
292             * @return   処ç�?µ�果のレポã?ãƒ?
293             */
294            public String report() {
295                    String report = "[" + getClass().getName() + "]" + CR
296                                    + TAB + "Copy Count : " + inCount   + CR
297                                    + TAB + "inPath     : " + inPath    + CR
298                                    + TAB + "inEncode   : " + inEncode  + CR
299                                    + TAB + "outPath    : " + outPath   + CR
300                                    + TAB + "outEncode  : " + outEncode + CR
301                                    + TAB + "binary     : " + binary ;
302    
303                    return report ;
304            }
305    
306            /**
307             * こã?クラスの使用方法を返しますã?
308             *
309             * @return      こã?クラスの使用方æ³?
310             */
311            public String usage() {
312                    StringBuilder buf = new StringBuilder();
313    
314                    buf.append( "Process_FileCopy は、上流から受け取っã�?FileLineModelをå?ç�?�™るã?"                                ).append( CR );
315                    buf.append( "ChainProcess インターフェースの実è£?‚¯ラスですã?"                                                              ).append( CR );
316                    buf.append( CR );
317                    buf.append( "上流から受け取っã�?FileLineModel の ファイルから、inPath の共通パス"                       ).append( CR );
318                    buf.append( "以下ã?ファイルをã?outPath の共通パス以下にコピã?しますã?"                                          ).append( CR );
319                    buf.append( "コピã?の種類ã?、バイナリかã?ãƒ?‚­ストで、テキストã?場合ã?、エンコーãƒ?                  ).append( CR );
320                    buf.append( "変換も行うことが可能ですã?"                                                                                                     ).append( CR );
321                    buf.append( "inPath と outPath が同じã?またã?、outPath が未設定ã?場合ã?、å?力と出力が"            ).append( CR );
322                    buf.append( "同じですã?で、è?åˆ??身のエンコード変換処ç�?‚’行うことになりますã?"                            ).append( CR );
323                    buf.append( CR );
324                    buf.append( "コピã?されるファイルのファイル名ã?、å?力ファイル名と同ä¸?�§すã?保存される"              ).append( CR );
325                    buf.append( "フォルãƒ?�Œ異なりますã?(同ä¸?�«することも可能ですã?)"                                                         ).append( CR );
326                    buf.append( CR );
327                    buf.append( "上流ã?ロセスでは、Name 属æ?として、ã?File』を持ち、å?は、Fileオブジェクãƒ?    ).append( CR );
328                    buf.append( "である、Process_FileSearch を使用するのがã?便利ですã?それ以外ã?クラスã‚?           ).append( CR );
329                    buf.append( "使用する場合でもã?Name属æ?と、File オブジェクトを持つ LineModel を受け渡ã�?  ).append( CR );
330                    buf.append( "できれば、使用可能ですã?"                                                                                                            ).append( CR );
331                    buf.append( CR );
332                    buf.append( "引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション(\"\") で括って下さã�??"         ).append( CR );
333                    buf.append( "引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に"             ).append( CR );
334                    buf.append( "繋げてくださいã€?                                                                                                                              ).append( CR );
335                    buf.append( CR ).append( CR );
336    
337                    buf.append( getArgument().usage() ).append( CR );
338    
339                    return buf.toString();
340            }
341    
342            /**
343             * こã?クラスは、main メソãƒ?ƒ‰から実行できませんã€?
344             *
345             * @param       args    コマンド引数配å?
346             */
347            public static void main( final String[] args ) {
348                    LogWriter.log( new Process_FileCopy().usage() );
349            }
350    }