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    }