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.fukurou.util;
017
018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
019import java.io.BufferedInputStream;
020import java.io.BufferedOutputStream;
021import java.io.BufferedReader;
022import java.io.BufferedWriter;
023import java.io.File;
024import java.io.InputStream;
025import java.io.FileInputStream;
026import java.io.FileNotFoundException;
027import java.io.FileOutputStream;
028import java.io.IOException;
029import java.io.OutputStream;
030import java.io.OutputStreamWriter;
031import java.io.PrintWriter;
032import java.io.UnsupportedEncodingException;
033import java.io.Writer;
034import java.util.Collections;
035import java.util.List;
036
037import java.nio.channels.FileChannel;
038import java.nio.file.Files;                                                                             // 6.2.0.0 (2015/02/27)
039import java.nio.charset.Charset;                                                                // 6.2.0.0 (2015/02/27)
040import org.opengion.fukurou.system.HybsConst;                                   // 6.4.5.2 (2016/05/06)
041
042import static org.opengion.fukurou.system.HybsConst.CR;                 // 6.1.0.0 (2014/12/26) refactoring
043import org.opengion.fukurou.system.Closer;                                              // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
044import org.opengion.fukurou.system.LogWriter;                                   // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
045import org.opengion.fukurou.system.ThrowUtil;                                   // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
046
047/**
048 * FileUtil.java は、共通的に使用される File関連メソッドを集約した、クラスです。
049 *
050 * 全変数は、public static final 宣言されており、全メソッドは、public static synchronized 宣言されています。
051 *
052 * @og.group ユーティリティ
053 *
054 * @version  4.0
055 * @author       Kazuhiko Hasegawa
056 * @since    JDK5.0,
057 */
058public final class FileUtil {
059        private static final NonClosePrintWriter OUT_WRITER = new NonClosePrintWriter( System.out );            // 6.4.1.1 (2016/01/16) outWriter → OUT_WRITER refactoring
060        private static final NonClosePrintWriter ERR_WRITER = new NonClosePrintWriter( System.err );            // 6.4.1.1 (2016/01/16) errWriter → ERR_WRITER refactoring
061
062        /** 5.6.1.2 (2013/02/22) UNIX系のファイル名を表すセパレータ文字  */
063
064        /** 5.6.1.2 (2013/02/22) Windwos系のファイル名を表すセパレータ文字       */
065
066        /** 5.6.1.2 (2013/02/22) ファイルの拡張子の区切りを表す文字      */
067        public static final char EXTENSION_SEPARATOR = '.';
068
069        private static final byte B_CR = (byte)0x0d ;   // '\r'
070        private static final byte B_LF = (byte)0x0a ;   // '\n'
071        private static final int  BUFSIZE = 8192 ;              // 5.1.6.0 (2010/05/01)
072
073        /**
074         * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。
075         *
076         */
077        private FileUtil() {}
078
079        /**
080         * Fileオブジェクトとエンコードより PrintWriterオブジェクトを作成します。
081         *
082         * @param       file    出力するファイルオブジェクト
083         * @param       encode  ファイルのエンコード
084         *
085         * @return      PrintWriterオブジェクト
086         * @throws RuntimeException 何らかのエラーが発生した場合
087         * @og.rtnNotNull
088         */
089        public static PrintWriter getPrintWriter( final File file,final String encode ) {
090                return getPrintWriter( file,encode,false );
091        }
092
093        /**
094         * Fileオブジェクトとエンコードより PrintWriterオブジェクトを作成します。
095         *
096         * @param       file    出力するファイルオブジェクト
097         * @param       encode  ファイルのエンコード
098         * @param       append  ファイルを追加モード(true)にするかどうか
099         *
100         * @return      PrintWriterオブジェクト
101         * @throws RuntimeException 何らかのエラーが発生した場合
102         * @og.rtnNotNull
103         */
104        public static PrintWriter getPrintWriter( final File file,final String encode,final boolean append ) {
105                final PrintWriter writer ;
106
107                try {
108                        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
109                                                        new FileOutputStream(file,append) ,encode )));
110                }
111                catch( final UnsupportedEncodingException ex ) {
112                        final String errMsg = "指定されたエンコーディングがサポートされていません。" + CR
113                                                        + ex.getMessage() + CR
114                                                        + "File=[" + file + " , encode=[" + encode + "]" ;
115                        throw new OgRuntimeException( errMsg,ex );
116                }
117                catch( final FileNotFoundException ex ) {               // 3.6.1.0 (2005/01/05)
118                        final String errMsg = "ファイル名がオープン出来ませんでした。" + CR
119                                                        + ex.getMessage() + CR
120                                                        + "File=[" + file + " , encode=[" + encode + "]" ;
121                        throw new OgRuntimeException( errMsg,ex );
122                }
123
124                return writer ;
125        }
126
127        /**
128         * ファイル名より、PrintWriterオブジェクトを作成する簡易メソッドです。
129         *
130         * これは、ファイル名は、フルパスで、追加モードで、UTF-8 エンコードの
131         * ログファイルを出力する場合に使用します。
132         * また、ファイル名に、"System.out" と、"System.err" を指定できます。
133         * その場合は、標準出力、または、標準エラー出力に出力されます。
134         * "System.out" と、"System.err" を指定した場合は、NonClosePrintWriter
135         * オブジェクトが返されます。これは、close() 処理が呼ばれても、何もしない
136         * クラスです。また、常に内部キャッシュの同じオブジェクトが返されます。
137         *
138         * @param       file    出力するファイル名
139         *
140         * @return      PrintWriterオブジェクト
141         * @throws RuntimeException 何らかのエラーが発生した場合
142         * @throws IllegalArgumentException ファイル名が null の場合
143         */
144        public static PrintWriter getLogWriter( final String file ) {
145                if( file == null ) {
146                        final String errMsg = "ファイル名に、null は指定できません。";
147                        throw new IllegalArgumentException( errMsg );
148                }
149
150                final PrintWriter writer ;
151                if( "System.out".equalsIgnoreCase( file ) ) {
152                        writer = OUT_WRITER ;
153                }
154                else if( "System.err".equalsIgnoreCase( file ) ) {
155                        writer = ERR_WRITER ;
156                }
157                else {
158                        writer = getPrintWriter( new File( file ),"UTF-8",true );
159                }
160
161                return writer ;
162        }
163
164        /**
165         * OutputStreamとエンコードより PrintWriterオブジェクトを作成します。
166         *
167         * @og.rev 5.5.2.0 (2012/05/01) 新規追加
168         *
169         * @param       os              利用するOutputStream
170         * @param       encode  ファイルのエンコード
171         *
172         * @return      PrintWriterオブジェクト
173         * @throws RuntimeException 何らかのエラーが発生した場合
174         */
175        public static PrintWriter getPrintWriter( final OutputStream os,final String encode ) {
176                final PrintWriter writer ;
177
178                try {
179                        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter( os ,encode )));
180                }
181                catch( final UnsupportedEncodingException ex ) {
182                        final String errMsg = "指定されたエンコーディングがサポートされていません。" + CR
183                                                        + ex.getMessage() + CR
184                                                        + "encode=[" + encode + "]" ;
185                        throw new OgRuntimeException( errMsg,ex );
186                }
187                return writer ;
188        }
189
190        /**
191         * PrintWriter を継承した、JspWriterなどの Writer 用のクラスを定義します。
192         *
193         * 例えば、JspWriterなどの JSP/Servlet等のフレームワークで使用される
194         * Writer では、flush や close 処理は、フレームワーク内で行われます。
195         * その場合、通常のファイルと同じ用に、flush や close をアプリケーション側で
196         * 行うと、内部処理的に不整合が発生したり、最悪の場合エラーになります。
197         * このクラスは、NonFlushPrintWriter クラスのオブジェクトを返します。
198         * これは、通常の、new PrintWriter( Writer ) で、求めるのと、ほとんど同様の
199         * 処理を行いますが、close() と flush() メソッドが呼ばれても、何もしません。
200         *
201         * @param       writer  出力するWriteオブジェクト(NonFlushPrintWriterクラス)
202         *
203         * @return      PrintWriterオブジェクト
204         * @og.rtnNotNull
205         */
206        public static PrintWriter getNonFlushPrintWriter( final Writer writer ) {
207                return new NonFlushPrintWriter( writer );
208        }
209
210        /**
211         * Fileオブジェクトとエンコードより BufferedReaderオブジェクトを作成します。
212         *
213         * これは、java 1.7 以降でしか使えませんが、FilesとPaths を使用した BufferedReader
214         * オブジェクトを返します。
215         * encode は、java.nio.charset.Charset になる為、従来のコードと異なるかも知れませんが、
216         * 日本語関係の判定をより正確に行う事が可能になります。(Windows-31J と UTF-8の判別など)
217         *
218         * @og.rev 6.2.0.0 (2015/02/27) java.nio.file.Files と、Paths を使用するように変更
219         *
220         * @param       file    入力するファイルオブジェクト
221         * @param       encode  ファイルのエンコード(java.nio.charset.Charset)
222         *
223         * @return      BufferedReaderオブジェクト
224         * @throws RuntimeException 何らかのエラーが発生した場合
225         * @og.rtnNotNull
226         */
227        public static BufferedReader getBufferedReader( final File file,final String encode ) {
228                final BufferedReader reader ;
229
230                try {
231                        reader = Files.newBufferedReader( file.toPath() , Charset.forName( encode ) );
232                }
233                catch( final IOException ex ) {
234                        final String errMsg = "ファイルのオープン中に入出力エラーが発生しました。" + CR
235                                                        + ex.getMessage() + CR
236                                                        + "File=[" + file + "] , encode=[" + encode + "]" ;
237                        throw new OgRuntimeException( errMsg,ex );
238                }
239                catch( final RuntimeException ex ) {
240                        final String errMsg = "指定された文字セットが不正か、現在のJava仮想マシンでは利用できません。" + CR
241                                                        + ex.getMessage() + CR
242                                                        + "File=[" + file + "] , encode=[" + encode + "]" ;
243                        throw new OgRuntimeException( errMsg,ex );
244                }
245
246                return reader ;
247        }
248
249        /**
250         * 指定のファイル名が、実際に存在しているかどうかをチェックします。
251         * 存在しない場合は、2秒毎に、3回確認します。
252         * それでも存在しない場合は、エラーを返します。
253         * return されるFileオブジェクトは、正規の形式(CanonicalFile)です。
254         *
255         * @param       dir                     フォルダ名
256         * @param       filename        ファイル名
257         *
258         * @return      存在チェック(なければ null/あれば、CanonicalFile)
259         */
260        public static File checkFile( final String dir, final String filename ) {
261                return checkFile( dir,filename,3 );
262        }
263
264        /**
265         * 指定のファイル名が、実際に存在しているかどうかをチェックします。
266         * 存在しない場合は、2秒毎に、指定の回数分確認します。
267         * それでも存在しない場合は、エラーを返します。
268         * return されるFileオブジェクトは、正規の形式(CanonicalFile)です。
269         *
270         * @param       dir                     フォルダ名
271         * @param       filename        ファイル名
272         * @param       count           回数指定
273         *
274         * @return      存在チェック(なければ null/あれば、CanonicalFile)
275         */
276        public static File checkFile( final String dir, final String filename,final int count ) {
277                File file = null;
278
279                int cnt = count;
280                while( cnt > 0 ) {
281                        file = new File( dir,filename );
282                        if( file.exists() ) { break; }
283                        else {
284                                if( cnt == 1 ) { return null; }         // 残り1回の場合は、2秒待機せずに即抜ける。
285                                try { Thread.sleep( 2000 );     }       // 2秒待機
286                                catch( final InterruptedException ex ) {
287                                        System.out.println( "InterruptedException" );
288                                }
289                                System.out.println();
290                                System.out.print( "CHECK File Error! CNT=" + cnt );
291                                System.out.print( " File=" + file.getAbsolutePath() );
292                        }
293                        cnt--;
294                }
295
296                // ファイルの正式パス名の取得
297                try {
298                        return file.getCanonicalFile() ;
299                }
300                catch( final IOException ex ) {
301                        final String errMsg = "ファイルの正式パス名が取得できません。[" + file.getAbsolutePath() + "]";
302                        throw new OgRuntimeException( errMsg,ex );
303                }
304        }
305
306        /**
307         * ファイルのバイナリコピーを行います。
308         *
309         * copy( File,File,false ) を呼び出します。
310         *
311         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
312         *
313         * @param       fromFile        コピー元ファイル名
314         * @param       toFile          コピー先ファイル名
315         *
316         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
317         * @see         #copy( File,File,boolean )
318         */
319        public static boolean copy( final String fromFile,final String toFile ) {
320                return copy( new File( fromFile ), new File( toFile ), false );
321        }
322
323        /**
324         * ファイルのバイナリコピーを行います。
325         *
326         * copy( File,File,boolean ) を呼び出します。
327         * 第3引数の、keepTimeStamp=true で、コピー元のファイルのタイムスタンプを、
328         * コピー先にもセットします。
329         *
330         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
331         *
332         * @param       fromFile        コピー元ファイル名
333         * @param       toFile          コピー先ファイル名
334         * @param       keepTimeStamp   タイムスタンプ維持[true/false]
335         *
336         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
337         * @see         #copy( File,File,boolean )
338         */
339        public static boolean copy( final String fromFile,final String toFile,final boolean keepTimeStamp ) {
340                return copy( new File( fromFile ), new File( toFile ), keepTimeStamp );
341        }
342
343        /**
344         * ファイルのバイナリコピーを行います。
345         *
346         * copy( File,File,false ) を呼び出します。
347         *
348         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
349         *
350         * @param       fromFile        コピー元ファイル
351         * @param       toFile          コピー先ファイル
352         *
353         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
354         * @see         #copy( File,File,boolean )
355         */
356        public static boolean copy( final File fromFile,final File toFile ) {
357                return copy( fromFile, toFile, false );
358        }
359
360        /**
361         * ファイルのバイナリコピーを行います。
362         *
363         * 第3引数の、keepTimeStamp=true で、コピー元のファイルのタイムスタンプを、
364         * コピー先にもセットします。
365         * toFile が、ディレクトリの場合は、fromFile のファイル名をそのままコピーします。
366         * fromFile がディレクトリの場合は、copyDirectry( File,Fileboolean )を call します。
367         *
368         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
369         * @og.rev 5.6.5.2 (2013/06/21) ByteBufferを利用した方式から、transferTo を使用する方式に変更
370         * @og.rev 5.7.1.2 (2013/12/20) copy先(toFile)のフォルダが存在しなければ、作成します。
371         * @og.rev 6.3.6.1 (2015/08/28) copy元(fromFile)がフォルダがディレクトリの場合は、#copyDirectry( File,File,boolean ) を呼ぶ。
372         * @og.rev 6.3.6.1 (2015/08/28) Exception発生時に return ではなく、StringUtil.ogErrMsg に変更。
373         * @og.rev 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
374         * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogErrMsgPrint(String,Throwable) を、ThrowUtilt#ogThrowMsg(String,Throwable) に変更。
375         *
376         * @param       fromFile        コピー元ファイル
377         * @param       toFile          コピー先ファイル
378         * @param       keepTimeStamp タイムスタンプ維持[true/false]
379         *
380         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
381         * @see         #copyDirectry( File,File,boolean )
382         */
383        public static boolean copy( final File fromFile,final File toFile,final boolean keepTimeStamp ) {
384                FileInputStream  inFile  = null;
385                FileOutputStream outFile = null;
386                FileChannel  fin  = null;
387                FileChannel  fout = null;
388
389                File tempToFile = toFile ;
390                try {
391                        // fromFileが、ディレクトリの場合は、エラー
392                        if( fromFile.isDirectory() ) {
393                                // 6.3.6.1 (2015/08/28)
394                                return copyDirectry( fromFile,toFile,keepTimeStamp );
395                        }
396                        // toFileが、ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。
397                        if( toFile.isDirectory() ) {
398                                tempToFile = new File( toFile,fromFile.getName() );
399                        }
400
401                        // 5.7.1.2 (2013/12/20) copy先(toFile)のフォルダが存在しなければ、作成します。
402                        final File parent = tempToFile.getParentFile();
403                        if( !parent.exists() && !parent.mkdirs() ) {
404                                // ディレクトリを作成する
405                                System.err.println( parent + " の ディレクトリ作成に失敗しました。" );
406                                return false;
407                        }
408
409                        inFile  = new FileInputStream( fromFile );
410                        outFile = new FileOutputStream( tempToFile );
411
412                        fin  = inFile.getChannel();
413                        fout = outFile.getChannel();
414
415                        // 5.6.5.2 (2013/06/21) ByteBufferを利用した方式から、transferTo を使用する方式に変更
416
417                        fin.transferTo(0, fin.size(), fout );
418                }
419                catch( final IOException ex ) {
420                        // 6.3.6.1 (2015/08/28) Exception発生時に return ではなく、StringUtil.ogErrMsg に変更。
421                        final String errMsg = "バイナリコピーで、エラーが発生しました。" + CR
422                                                        + "fromFile=[" + fromFile + "]" + CR
423                                                        + "toFile  =[" + toFile   + "]" + CR ;
424                        // 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
425                        System.out.println( ThrowUtil.ogThrowMsg( errMsg , ex ) );                      // 6.4.2.0 (2016/01/29)
426                        return false;
427                }
428                finally {
429                        Closer.ioClose( inFile  ) ;
430                        Closer.ioClose( outFile );
431                        Closer.ioClose( fin  ) ;
432                        Closer.ioClose( fout );
433                }
434
435                if( keepTimeStamp ) {
436                        return tempToFile.setLastModified( fromFile.lastModified() );
437                }
438
439                return true;
440        }
441
442        /**
443         * ファイルのバイナリコピーを行います。
444         *
445         * このファイルコピーは、バイナリファイルの 改行コードを
446         * CR+LF に統一します。また、UTF-8 の BOM(0xef,0xbb,0xbf) があれば、
447         * 取り除きます。
448         *
449         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
450         * @og.rev 6.3.6.1 (2015/08/28) Exception発生時に return ではなく、StringUtil.ogErrMsg に変更。
451         * @og.rev 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
452         * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogErrMsgPrint(String,Throwable) を、ThrowUtilt#ogThrowMsg(String,Throwable) に変更。
453         *
454         * @param       fromFile        コピー元ファイル
455         * @param       toFile          コピー先ファイル
456         *
457         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
458         */
459        public static boolean changeCrLfcopy( final File fromFile,final File toFile ) {
460                BufferedInputStream     fromStream = null;
461                BufferedOutputStream    toStream   = null;
462                File tempToFile = toFile ;
463                try {
464                        // ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。
465                        if( toFile.isDirectory() ) {
466                                tempToFile = new File( toFile,fromFile.getName() );
467                        }
468                        fromStream = new BufferedInputStream( new FileInputStream( fromFile ) );
469                        toStream   = new BufferedOutputStream( new FileOutputStream( tempToFile ) );
470
471                        final byte[] buf = new byte[BUFSIZE];
472                        int len ;
473                        // 4.2.3.0 (2008/05/26) changeCrLf 属性対応
474
475                        boolean bomCheck = true;        // 最初の一回だけ、BOMチェックを行う。
476                        byte    bt = (byte)0x00;        // バッファの最後と最初の比較時に使用
477                        while( (len = fromStream.read(buf,0,BUFSIZE)) != -1 ) {
478                                int st = 0;
479                                if( bomCheck && len >= 3 &&
480                                        buf[0] == (byte)0xef &&
481                                        buf[1] == (byte)0xbb &&
482                                        buf[2] == (byte)0xbf  ) {
483                                                st = 3;
484                                }
485                                else {
486                                        // バッファの最後が CR で、先頭が LF の場合、LF をパスします。
487                                        if( bt == B_CR && buf[0] == B_LF ) {
488                                                st = 1 ;
489                                        }
490                                }
491                                bomCheck = false;
492
493                                for( int i=st;i<len;i++ ) {
494                                        bt = buf[i] ;
495                                        if( bt == B_CR || bt == B_LF ) {
496                                                toStream.write( (int)B_CR );            // CR
497                                                toStream.write( (int)B_LF );            // LF
498                                                // CR+LF の場合
499                                                if( bt == B_CR && i+1 < len && buf[i+1] == B_LF ) {
500                                                        i++;
501                                                        bt = buf[i] ;
502                                                }
503                                        }
504                                        else {
505                                                toStream.write( (int)bt );
506                                        }
507                                }
508                        }
509                        // 最後が改行コードでなければ、改行コードを追加します。
510                        // テキストコピーとの互換性のため
511                        if( bt != B_CR && bt != B_LF ) {
512                                toStream.write( (int)B_CR );            // CR
513                                toStream.write( (int)B_LF );            // LF
514                        }
515                }
516                catch( final IOException ex ) {
517                        // 6.3.6.1 (2015/08/28) Exception発生時に return ではなく、StringUtil.ogErrMsg に変更。
518                        final String errMsg = "バイナリコピー(CrLf)で、エラーが発生しました。" + CR
519                                                        + "fromFile=[" + fromFile + "]" + CR
520                                                        + "toFile  =[" + toFile   + "]" + CR ;
521                        // 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
522                        System.out.println( ThrowUtil.ogThrowMsg( errMsg , ex ) );                      // 6.4.2.0 (2016/01/29)
523                        return false;
524                }
525                finally {
526                        Closer.ioClose( fromStream ) ;
527                        Closer.ioClose( toStream ) ;
528                }
529
530                return true;
531        }
532
533        /**
534         * ファイルのバイナリコピーを行います。
535         *
536         * コピー元のファイルは、InputStream で指定します。
537         * 元は、jsp/common 以下を圧縮、jar化したため、物理ファイルの取得が
538         * できなくなったため、ServletContext#getServletContext() で、ローカルリソースを
539         * 取得するのが目的です。汎用的に、入力は、InputStream にしました。
540         * URLConnection 等で、取得する場合は、BASIC認証も考慮する必要がありますので、
541         * ご注意ください。
542         * タイムスタンプのコピーは行いません。
543         *
544         * @og.rev 6.3.6.1 (2015/08/28) InputStreamで指定されたファイルのコピー
545         * @og.rev 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
546         * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogErrMsgPrint(String,Throwable) を、ThrowUtilt#ogThrowMsg(String,Throwable) に変更。
547         *
548         * @param       inStrm          コピー元のInputStream(この中でcloseします)
549         * @param       toFile          コピー先ファイル
550         *
551         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
552         * @see         #copy( File,File )
553         */
554        public static boolean copy( final InputStream inStrm,final File toFile ) {
555                FileOutputStream foStrm = null;
556                try {
557                        // copy先(toFile)のフォルダが存在しなければ、作成します。
558                        final File parent = toFile.getParentFile();
559                        if( !parent.exists() && !parent.mkdirs() ) {
560                                // ディレクトリを作成する
561                                System.err.println( parent + " の ディレクトリ作成に失敗しました。" );
562                                return false;
563                        }
564
565                        foStrm = new FileOutputStream( toFile, false );
566                        return copy( inStrm , foStrm );
567                }
568                catch( final IOException ex ) {
569                        // 6.3.6.1 (2015/08/28) Exception発生時に return ではなく、StringUtil.ogErrMsg に変更。
570                        final String errMsg = "入力ストリームのコピーでエラーが発生しました。" + CR
571                                                        + "toFile  =[" + toFile   + "]" + CR ;
572                        // 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
573                        System.out.println( ThrowUtil.ogThrowMsg( errMsg , ex ) );                      // 6.4.2.0 (2016/01/29)
574                }
575                finally {
576                        Closer.ioClose( inStrm );
577                        Closer.ioClose( foStrm );
578                }
579
580                return false ;
581        }
582
583        /**
584         * 入出力ストリーム間でデータの転送を行います。
585         *
586         * ここでは、すでに作成されたストリームに基づき、データの入出力を行います。
587         * よって、先にフォルダ作成や、存在チェック、ファイルの削除などの必要な処理は
588         * 済まして置いてください。
589         * また、このメソッド内で、ストリームのクロース処理は行っていません。
590         *
591         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
592         * @og.rev 6.3.6.1 (2015/08/28) エラー時のメッセージ情報を増やします。
593         * @og.rev 6.3.8.5 (2015/10/16) StringUtil#ogErrMsgPrint 使用。
594         * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogErrMsgPrint(String,Throwable) を、ThrowUtilt#ogThrowMsg(String,Throwable) に変更。
595         *
596         * @param       input   入力ストリーム
597         * @param       output  出力ストリーム
598         *
599         * @return      データ転送が正常に終了したかどうか[true:成功/false:失敗]
600         */
601        public static boolean copy( final InputStream input,final OutputStream output ) {
602                if( input == null ) {
603                        final String errMsg = "入力ストリームが 作成されていません。" ;
604                        // 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
605                        System.out.println( ThrowUtil.ogThrowMsg( errMsg ) );                   // 6.4.2.0 (2016/01/29)
606                        return false;
607                }
608
609                if( output == null ) {
610                        final String errMsg = "出力ストリームが 作成されていません。" ;
611                        // 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
612                        System.out.println( ThrowUtil.ogThrowMsg( errMsg ) );                   // 6.4.2.0 (2016/01/29)
613                        return false;
614                }
615
616                try {
617                        final byte[] buf = new byte[BUFSIZE];
618                        int len;
619                        while((len = input.read(buf)) != -1) {
620                                output.write(buf, 0, len);
621                        }
622                }
623                catch( final IOException ex ) {
624                        final String errMsg = "ストリームデータの入出力処理に失敗しました。";
625                        // 6.3.8.5 (2015/10/16) StringUtil.ogErrMsgPrint 使用。
626                        System.out.println( ThrowUtil.ogThrowMsg( errMsg,ex ) );                        // 6.4.2.0 (2016/01/29)
627                        return false;
628                }
629        //      finally {
630        //              Closer.ioClose( input );
631        //              Closer.ioClose( output );
632        //      }
633                return true ;
634        }
635
636        /**
637         * 再帰処理でディレクトリのコピーを行います。
638         *
639         * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。
640         *
641         * @og.rev 4.3.0.0 (2008/07/24) 追加
642         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
643         *
644         * @param       fromDir コピー元ディレクトリ名
645         * @param       toDir   コピー先ディレクトリ名
646         *
647         * @return      ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗]
648         */
649        public static boolean copyDirectry( final String fromDir, final String toDir ) {
650                return copyDirectry( new File( fromDir ), new File( toDir ),false );
651        }
652
653        /**
654         * 再帰処理でディレクトリをコピーします。
655         *
656         * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。
657         *
658         * @og.rev 4.3.0.0 (2008/07/24) 追加
659         * @og.rev 5.1.6.0 (2010/05/01) 内部処理を若干変更します。
660         *
661         * @param       fromDir   コピー元ディレクトリ
662         * @param       toDir     コピー先ディレクトリ
663         *
664         * @return      ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗]
665         */
666        public static boolean copyDirectry( final File fromDir, final File toDir ) {
667                return copyDirectry( fromDir, toDir, false );
668        }
669
670        /**
671         * 再帰処理でディレクトリをコピーします。
672         *
673         * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。
674         *
675         * @og.rev 4.3.0.0 (2008/07/24) 追加
676         * @og.rev 5.1.6.0 (2010/05/01) 内部処理を若干変更します。
677         * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。
678         *
679         * @param       fromDir コピー元ディレクトリ
680         * @param       toDir   コピー先ディレクトリ
681         * @param       keepTimeStamp タイムスタンプ維持[true/false]
682         *
683         * @return      ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗]
684         */
685        public static boolean copyDirectry( final File fromDir, final File toDir, final boolean keepTimeStamp ) {
686                // コピー元がディレクトリでない場合はfalseを返す
687                // 4.3.4.4 (2009/01/01)
688                if( !fromDir.exists() || !fromDir.isDirectory() ) {
689                        System.err.println( fromDir + " が ディレクトリでないか、存在しません。" );
690                        return false;
691                }
692
693                // 4.3.4.4 (2009/01/01) ディレクトリを作成する
694                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
695                if( !toDir.exists() && !toDir.mkdirs() ) {
696                        System.err.println( toDir + " の ディレクトリ作成に失敗しました。" );
697                        return false;
698                }
699
700                // ディレクトリ内のファイルをすべて取得する
701                final File[] files = fromDir.listFiles();
702
703                // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラー
704                if( files == null ) {
705                        System.err.println( fromDir + " はアクセスできません。" );
706                        return false;
707                }
708
709                // ディレクトリ内のファイルに対しコピー処理を行う
710                boolean flag = true;
711                for( int i=0; files.length>i; i++ ){
712                        if( files[i].isDirectory() ){ // ディレクトリだった場合は再帰呼び出しを行う
713                                flag = copyDirectry( files[i], new File( toDir, files[i].getName()),keepTimeStamp );
714                        }
715                        else{ // ファイルだった場合はファイルコピー処理を行う
716                                flag = copy( files[i], new File( toDir, files[i].getName()),keepTimeStamp );
717                        }
718                        if( !flag ) { return false; }
719                }
720                return true;
721        }
722
723        /**
724         * 指定されたファイル及びディレクトを削除します。
725         * ディレクトリの場合はサブフォルダ及びファイルも削除します。
726         * 1つでもファイルの削除に失敗した場合、その時点で処理を中断しfalseを返します。
727         *
728         * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。
729         *
730         * @param       file 削除ファイル/ディレクトリ
731         *
732         * @return      ファイル/ディレクトリの削除に終了したかどうか[true:成功/false:失敗]
733         */
734        public static boolean deleteFiles( final File file ) {
735                if( file.exists() ) {
736                        if( file.isDirectory() ) {
737                                final File[] list = file.listFiles();
738
739                                // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラー
740                                if( list == null ) {
741                                        System.err.println( file + " はアクセスできません。" );
742                                        return false;
743                                }
744
745                                for( int i=0; i<list.length; i++ ) {
746                                        deleteFiles( list[i] );
747                                }
748                        }
749                        if( !file.delete() ) { return false; }
750                }
751                return true;
752        }
753
754        /**
755         * 指定されたディレクトリを基点としたファイル名(パスを含む)の一覧を返します。
756         *
757         * @og.rev 4.3.6.6 (2009/05/15) 新規作成
758         * @og.rev 5.4.3.2 (2012/01/06) 引数isCopy追加
759         * @og.rev 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。
760         *
761         * @param dir 基点となるディレクトリ
762         * @param sort ファイル名でソートするか
763         * @param list ファイル名一覧を格納するList
764         * @param isCopy コピー中ファイルを除外するか [true:含む/false:除外]
765         */
766        public static void getFileList( final File dir, final boolean sort, final List<String> list, final boolean isCopy ) {
767                if( list == null ) { return; }
768                if( dir.isFile() ) {
769                        // コピー中判定はrenameで行う
770                        // 6.1.0.0 (2014/12/26) refactoring : Avoid if(x != y) ..; else ..;
771                        if( isCopy || dir.renameTo( dir ) ) {
772                                list.add( dir.getAbsolutePath() );
773                        }
774                        else{
775                                return;
776                        }
777                }
778                else if( dir.isDirectory() ) {
779                        final File[] files = dir.listFiles();
780                        // 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。
781                        if( files != null ) {
782                                for( int i=0; i<files.length; i++ ) {
783                                        getFileList( files[i], sort, list, isCopy );
784                                }
785                        }
786                }
787                if( sort ) {
788                        Collections.sort( list );
789                }
790        }
791
792        /**
793         * 指定されたディレクトリを基点としたファイル名(パスを含む)の一覧を返します。
794         * 互換性のため、コピー中ファイルも含みます。
795         *
796         * @og.rev 5.4.3.2 (2012/01/06) コピー中対応のため引数4つを作成する
797         *
798         * @param dir 基点となるディレクトリ
799         * @param sort ファイル名でソートするか
800         * @param list ファイル名一覧を格納するList
801         */
802        public static void getFileList( final File dir, final boolean sort, final List<String> list ) {
803                getFileList( dir, sort, list, true );
804        }
805
806        /**
807         * ファイルをリネームを行います。
808         * 引数のuseBackup属性を true にすると、toFile が存在した場合、toFile の直下に "_backup" フォルダを
809         * 作成して、toFile + "_" + (現在時刻のLONG値) + "." + (toFileの拡張子) に名前変更します。
810         * useBackup属性を rename にすると、toFile が存在した場合、toFile に、"_001" からなる
811         * 連番を付与し、重複しなくなるまで、名前を変更します。
812         * useBackup属性を false(またはnull) にすると、toFile が存在した場合、toFile を削除します。
813         *
814         * 戻り値は、変更後のファイルオブジェクトです。
815         *
816         * @og.rev 5.7.1.2 (2013/12/20) 新規追加
817         * @og.rev 6.0.2.4 (2014/10/17) useBackup の機能追加
818         * @og.rev 6.2.0.0 (2015/02/27) FileInfoクラスを使用。 (#getFileSplit(File)の結果配列は廃止)
819         *
820         * @param       fromFile        名前変更する元のファイル
821         * @param       toFile          名前変更後のファイル
822         * @param       useBackup       置き換えファイルの処理方法(true:backupフォルダ/false:しない/rename:重複しない連番)
823         * @return      名前変更後のファイル
824         * @throws      RuntimeException        名称変更処理ができなかった場合。
825         */
826        public static File renameTo( final File fromFile , final File toFile , final String useBackup ) {
827                if( fromFile == null || toFile == null ) {
828                        final String errMsg = "入力ファイルが null です。from=[" + fromFile + "] , to=[" + toFile + "]" ;
829                        throw new OgRuntimeException( errMsg );
830                }
831
832                final File parent = toFile.getParentFile();                     // 6.0.2.4 (2014/10/17) toFile のフォルダがなければ作成
833                if( !parent.exists() && !parent.mkdirs() ) {
834                        final String errMsg = "toファイルのフォルダが作成できません。from=[" + fromFile + "] , to=[" + toFile + "]" ;
835                        throw new OgRuntimeException( errMsg );
836                }
837
838                // 変更先のファイルが存在した場合の処理。
839                File newFile = toFile;                  // useBackup = "rename" の時のみ書き換えたいので。
840                if( toFile.exists() ) {
841                        final FileInfo info = new FileInfo( toFile );                           // 6.2.0.0 (2015/02/27)
842                        // バックアップ作成する場合
843                        // 6.0.2.4 (2014/10/17) useBackup は、文字列で、true/false,(null)/rename がある。
844                        if( "true".equalsIgnoreCase( useBackup ) ) {
845                                final File backup = new File( parent , "_backup" );     // その直下に、"_backup" フォルダを作成
846                                if( !backup.exists() && !backup.mkdirs() ) {
847                                        final String errMsg = "バックアップ処理でbackupフォルダの作成に失敗しました。[" + backup + "]";
848                                        throw new OgRuntimeException( errMsg );
849                                }
850                                // バックアップファイル名は、元のファイル名(拡張子含む) + "_" + 現在時刻のlong値 + "." + 元のファイルの拡張子
851                                final String toName = toFile.getName();
852                                final File toFile2  = new File( parent,toName );                // オリジナルの toFile をrename するとまずいので、同名のFileオブジェクトを作成
853
854                                final String bkupName = info.NAME + "_" + System.currentTimeMillis() + "."  + info.SUFIX ;              // 6.2.0.0 (2015/02/27)
855                                final File bkupFile = new File( backup,bkupName );
856
857                                if( !toFile2.renameTo( bkupFile ) ) {
858                                        final String errMsg = "バックアップ処理でバックアップファイルをリネームできませんでした。" +CR
859                                                                                 + "  [" + toFile + "] ⇒ [" + bkupFile + "]" ;
860                                        throw new OgRuntimeException( errMsg );
861                                }
862                        }
863                        // 他と違い、toFile を変更する必要がある。
864                        else if( "rename".equalsIgnoreCase( useBackup ) ) {
865                                for( int i=1000; i<2000; i++ ) {                        // 000 の3桁を取得したいため。
866                                        final String no = String.valueOf( i ).substring(1);
867                                        // 6.2.0.0 (2015/02/27) 配列ではなく、FileInfoクラスを使用
868                                        final File toFile2 = new File( info.DIR , info.NAME + "_" + no + "." + info.SUFIX );
869                                        if( !toFile2.exists() ) {
870                                                newFile = toFile2;
871                                                break;
872                                        }
873                                }
874                        }
875                        // バックアップ作成しない場合は、削除します。
876                        else if( !toFile.delete() ) {
877                                final String errMsg = "既存のファイル[" + toFile + "]が削除できませんでした。";
878                                throw new OgRuntimeException( errMsg );
879                        }
880                }
881
882                if( !fromFile.renameTo( newFile ) ) {
883                        final String errMsg = "所定のファイルをリネームできませんでした。" + CR
884                                                                + "  [" + fromFile + "] ⇒ [" + newFile + "]" ;
885                        throw new OgRuntimeException( errMsg );
886                }
887                return newFile;
888        }
889
890        /**
891         * ファイルを読み取って、文字列を作成します。
892         *
893         * データの読取が完全に出来なかったときには、途中までのデータを返します。
894         * 指定のエンコードが存在しない場合や、ファイルが存在しない場合は、
895         * OgRuntimeException を throw します。
896         * encode が null の場合は、UTF-8 で読み込みます。
897         *
898         * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.StringUtil → fukurou.system.HybsConst に変更
899         * @og.rev 6.4.5.1 (2016/04/28) encode は初期化しているため、null はセットされません。
900         * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。
901         *
902         * @param  filename ファイル名
903         * @param  encode エンコード名
904         * @return  ファイルを読み取った文字列
905         * @throws RuntimeException 指定のエンコードが存在しなかったとき。
906         */
907        public static String getValue( final String filename , final String encode ) {
908                if( filename == null ) {
909                        final String errMsg = "ファイル名が指定されていません。" ;
910                        throw new OgRuntimeException( errMsg );
911                }
912
913                final String enc = encode == null ? HybsConst.UTF_8 : encode ;
914
915                try {
916                        return new String( Files.readAllBytes( new File( filename ).toPath() ),enc );
917                }
918                catch( final IOException ex ) {
919                        final String errMsg = "ファイル名がオープン出来ませんでした。[" + filename + "]" ;
920                        throw new OgRuntimeException( errMsg,ex );
921                }
922        }
923
924        /**
925         * 改行コードで分割して、Listオブジェクトを返します。
926         *
927         * encode が null の場合は、UTF-8 で読み込みます。
928         *
929         * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。
930         *
931         * @param  filename ファイル名
932         * @param  encode エンコード名
933         * @return  ファイルを読み取った文字列を分割したList
934         */
935        public static List<String> getLineList( final String filename , final String encode ) {
936                try {
937                        final String enc = encode == null ? HybsConst.UTF_8 : encode ;
938
939                        return Files.readAllLines( new File( filename ).toPath() , Charset.forName( enc ) );
940                }
941                catch( final IOException ex ) {
942                        final String errMsg = "ファイル名がオープン出来ませんでした。[" + filename + "]" ;
943                        throw new OgRuntimeException( errMsg,ex );
944                }
945        }
946
947        /**
948         * Fileオブジェクトのサイズを返します。
949         *
950         * オブジェクトが通常のファイルの場合は、そのファイルサイズを返します。
951         * フォルダの場合は、再帰的に、ファイルサイズを加算した結果を返します。
952         *
953         * @og.rev 6.7.4.1 (2017/02/17) Fileオブジェクトのサイズを返します。
954         *
955         * @param  file Fileオブジェクト
956         * @return  ファイルまたはフォルダののサイズ
957         */
958        public static long length( final File file ) {
959                if( file.isDirectory() ) {
960                        final File[] children = file.listFiles();
961
962                        long sum = 0;
963                        if( children != null ) {
964                                for( final File child : children ) {
965                                        sum += length( child );
966                                }
967                        }
968                        return sum;
969                }
970                return file.length();
971        }
972
973        /**
974         * PrintWriter を継承した、System.out/System.err 用のクラスを定義します。
975         *
976         * 通常の、new PrintWriter( OutputStream ) で、求めるのと、ほとんど同様の
977         * 処理を行います。
978         * ただ、close() メソッドが呼ばれても、何もしません。
979         *
980         */
981        private static final class NonClosePrintWriter extends PrintWriter {
982                /**
983                 * コンストラクター
984                 *
985                 * new PrintWriter( OutputStream ) を行います。
986                 *
987                 * @param out OutputStreamオブジェクト
988                 */
989                public NonClosePrintWriter( final OutputStream out ) {
990                        super( out );
991                }
992
993                /**
994                 * close() メソッドをオーバーライドします。
995                 *
996                 * 何もしません。
997                 */
998                @Override
999                public void close() {
1000                        // ここでは処理を行いません。
1001                }
1002        }
1003
1004        /**
1005         * PrintWriter を継承した、JspWriterなどの Writer 用のクラスを定義します。
1006         *
1007         * 例えば、JspWriterなどの JSP/Servlet等のフレームワークで使用される
1008         * Writer では、flush や close 処理は、フレームワーク内で行われます。
1009         * その場合、通常のファイルと同じ用に、flush や close をアプリケーション側で
1010         * 行うと、内部処理的に不整合が発生したり、最悪の場合エラーになります。
1011         * このクラスは、単に、通常の、new PrintWriter( Writer ) で、求めるのと、
1012         * ほとんど同様の処理を行います。
1013         * ただ、close() と flush() メソッドが呼ばれても、何もしません。
1014         *
1015         */
1016        private static final class NonFlushPrintWriter extends PrintWriter {
1017                /**
1018                 * コンストラクター
1019                 *
1020                 * new PrintWriter( Writer ) を行います。
1021                 *
1022                 * @param writer Writerオブジェクト
1023                 */
1024                public NonFlushPrintWriter( final Writer writer ) {
1025                        super( writer );
1026                }
1027
1028                /**
1029                 * close() メソッドをオーバーライドします。
1030                 *
1031                 * 何もしません。
1032                 */
1033                @Override
1034                public void close() {
1035                        // ここでは処理を行いません。
1036                }
1037
1038                /**
1039                 * flush() メソッドをオーバーライドします。
1040                 *
1041                 * 何もしません。
1042                 */
1043                @Override
1044                public void flush() {
1045                        // ここでは処理を行いません。
1046                }
1047        }
1048
1049        /**
1050         * ファイルのエンコードを変換するコピーを行います。
1051         *
1052         * copy( File,File,false ) を呼び出します。
1053         *
1054         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
1055         *
1056         * @param       file1   コピー元ファイル名
1057         * @param       file2   コピー先ファイル名
1058         * @param       encode1 コピー元ファイルのエンコード
1059         * @param       encode2 コピー先ファイルのエンコード
1060         *
1061         * @see         #copy( File,File,boolean )
1062         */
1063        public static void copy( final File file1,final File file2,final String encode1,final String encode2 ) {
1064        //      final File tempFile = new File( file2.getName() + "_backup" );
1065
1066        //      FileUtil.copy( file2,tempFile );
1067
1068                final BufferedReader reader = FileUtil.getBufferedReader( file1 ,encode1 );
1069                final PrintWriter    writer = FileUtil.getPrintWriter(    file2 ,encode2 );
1070
1071                try {
1072                        String line1;
1073                        while((line1 = reader.readLine()) != null) {
1074                                writer.println( line1 );
1075                        }
1076                }
1077                catch( final Throwable th ) {
1078                        th.printStackTrace();
1079                }
1080                finally {
1081                        Closer.ioClose( reader ) ;
1082                        Closer.ioClose( writer ) ;
1083                }
1084
1085                // 6.9.8.0 (2018/05/28) FindBugs:例外的戻り値を無視しているメソッド
1086//              file2.setLastModified( file1.lastModified() );
1087                if( !file2.setLastModified( file1.lastModified() ) ) {
1088                        final String errMsg = "FileUtil.copy において、タイムスタンプの更新が出来ませんでした。" + CR
1089                                                                        + " file2= [" + file2 + "]" + CR ;
1090                        System.err.println( errMsg );
1091                }
1092        }
1093
1094        /**
1095         * ファイルをコピーします。
1096         *
1097         * 引数に &lt;file1&gt; &lt;file2&gt; [&lt;encode1&gt; &lt;encode2&gt;] を指定します。
1098         * file1 を読み込み、file2 にコピーします。コピー前に、file2 は、file2_backup にコピーします。
1099         * file1 が、ディレクトリの場合は、ディレクトリごとコピーします。
1100         * encode1、encode2 を指定すると、エンコード変換しながらコピーになります。
1101         * この場合は、ファイル同士のコピーのみになります。
1102         *
1103         * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。
1104         * @og.rev 5.1.6.0 (2010/05/01) 引数の並び順、処理を変更します。
1105         *
1106         * @param       args 引数配列  file1 file2 [encode1 encode2]
1107         * @throws Throwable なんらかのエラーが発生した場合。
1108         */
1109        public static void main( final String[] args ) throws Throwable {
1110                if( args.length != 2 && args.length != 4 ) {
1111                        LogWriter.log("Usage: java org.opengion.fukurou.util.FileUtil <file1> <file2> [<encode1> <encode2>]" );
1112                        return ;
1113                }
1114
1115                final File file1 = new File( args[0] );
1116                final File file2 = new File( args[1] );
1117
1118                if( args.length < 3 ) {
1119                        if( file1.isDirectory() ) {
1120                                FileUtil.copyDirectry( file1, file2, true );
1121                        }
1122                        else {
1123                                final File tempFile = new File( args[1] + "_backup" );
1124                                FileUtil.copy( file2,tempFile );
1125                                FileUtil.copy( file1,file2, true );
1126                        }
1127                }
1128                else {
1129                        final String encode1 = args[2];
1130                        final String encode2 = args[3];
1131
1132                        if( file1.isDirectory() ) {
1133                                final File[] children = file1.listFiles();
1134
1135                                if( children != null ) {
1136                                        for( final File child : children ) {
1137                                                copy( child , new File( file2 , child.getName() ),encode1,encode2 );
1138                                        }
1139                                }
1140                        }
1141                        else {
1142                                copy( file1,file2,encode1,encode2 );
1143                        }
1144                }
1145        }
1146}