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