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