    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 java.io.BufferedInputStream;
    019import java.io.BufferedOutputStream;
    020import java.io.BufferedReader;
    021import java.io.BufferedWriter;
    022import java.io.File;
    023import java.io.InputStream;
    024import java.io.FileInputStream;
    025import java.io.FileNotFoundException;
    026import java.io.FileOutputStream;
    027import java.io.IOException;
    028import java.io.InputStreamReader;
    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;
    038
    039/**
    040 * FileUtil.java は、共通的に使用される File関連メソッドを集約した、クラスです。
    041 *
    042 * 全変数は、public static final 宣言されており、全メソッドは、public static synchronized 宣言されています。
    043 *
    044 * @og.group ユーティリティ
    045 *
    046 * @version  4.0
    047 * @author       Kazuhiko Hasegawa
    048 * @since    JDK5.0,
    049 */
    050public final class FileUtil {
    051        private static final NonClosePrintWriter outWriter = new NonClosePrintWriter( System.out );
    052        private static final NonClosePrintWriter errWriter = new NonClosePrintWriter( System.err );
    053
    054        /** システム依存の改行記号をセットします。 */
    055        private static final String CR = System.getProperty("line.separator");
    056
    057        /** 5.6.1.2 (2013/02/22) UNIX系のファイル名を表すセパレータ文字  */
    058        private static final char UNIX_SEPARATOR = '/';
    059
    060        /** 5.6.1.2 (2013/02/22) Windwos系のファイル名を表すセパレータ文字       */
    061        private static final char WINDOWS_SEPARATOR = '\\';
    062
    063        /** 5.6.1.2 (2013/02/22) ファイルの拡張子の区切りを表す文字      */
    064        public static final char EXTENSION_SEPARATOR = '.';
    065
    066        private static final byte B_CR = (byte)0x0d ;   // '\r'
    067        private static final byte B_LF = (byte)0x0a ;   // '\n'
    068        private static final int  BUFSIZE = 8192 ;              // 5.1.6.0 (2010/05/01)
    069
    070        /**
    071         * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。
    072         *
    073         */
    074        private FileUtil() {}
    075
    076        /**
    077         * Fileオブジェクトとエンコードより PrintWriterオブジェクトを作成します。
    078         *
    079         * @param       file    出力するファイルオブジェクト
    080         * @param       encode  ファイルのエンコード
    081         *
    082         * @return      PrintWriterオブジェクト
    083         * @throws RuntimeException 何らかのエラーが発生した場合
    084         */
    085        public static PrintWriter getPrintWriter( final File file,final String encode ) {
    086                return getPrintWriter( file,encode,false );
    087        }
    088
    089        /**
    090         * Fileオブジェクトとエンコードより PrintWriterオブジェクトを作成します。
    091         *
    092         * @param       file    出力するファイルオブジェクト
    093         * @param       encode  ファイルのエンコード
    094         * @param       append  ファイルを追加モード(true)にするかどうか
    095         *
    096         * @return      PrintWriterオブジェクト
    097         * @throws RuntimeException 何らかのエラーが発生した場合
    098         */
    099        public static PrintWriter getPrintWriter( final File file,final String encode,final boolean append ) {
    100                final PrintWriter writer ;
    101
    102                try {
    103                        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    104                                        new FileOutputStream(file,append) ,encode )));
    105                }
    106                catch( UnsupportedEncodingException ex ) {
    107                        String errMsg = "指定されたエンコーディングがサポートされていません。" + CR
    108                                                        + ex.getMessage() + CR
    109                                                        + "File=[" + file + " , encode=[" + encode + "]" ;
    110                        throw new RuntimeException( errMsg,ex );
    111                }
    112                catch( FileNotFoundException ex ) {             // 3.6.1.0 (2005/01/05)
    113                        String errMsg = "ファイル名がオープン出来ませんでした。" + CR
    114                                                        + ex.getMessage() + CR
    115                                                        + "File=[" + file + " , encode=[" + encode + "]" ;
    116                        throw new RuntimeException( errMsg,ex );
    117                }
    118
    119                return writer ;
    120        }
    121
    122        /**
    123         * ファイル名より、PrintWriterオブジェクトを作成する簡易メソッドです。
    124         *
    125         * これは、ファイル名は、フルパスで、追加モードで、UTF-8 エンコードの
    126         * ログファイルを出力する場合に使用します。
    127         * また、ファイル名に、"System.out" と、"System.err" を指定できます。
    128         * その場合は、標準出力、または、標準エラー出力に出力されます。
    129         * "System.out" と、"System.err" を指定した場合は、NonClosePrintWriter
    130         * オブジェクトが返されます。これは、close() 処理が呼ばれても、何もしない
    131         * クラスです。また、常に内部キャッシュの同じオブジェクトが返されます。
    132         *
    133         * @param       file    出力するファイル名
    134         *
    135         * @return      PrintWriterオブジェクト
    136         * @throws RuntimeException 何らかのエラーが発生した場合
    137         * @throws IllegalArgumentException ファイル名が null の場合
    138         */
    139        public static PrintWriter getLogWriter( final String file ) {
    140                if( file == null ) {
    141                        String errMsg = "ファイル名に、null は指定できません。";
    142                        throw new IllegalArgumentException( errMsg );
    143                }
    144
    145                final PrintWriter writer ;
    146                if( "System.out".equalsIgnoreCase( file ) ) {
    147                        writer = outWriter ;
    148                }
    149                else if( "System.err".equalsIgnoreCase( file ) ) {
    150                        writer = errWriter ;
    151                }
    152                else {
    153                        writer = getPrintWriter( new File( file ),"UTF-8",true );
    154                }
    155
    156                return writer ;
    157        }
    158
    159        /**
    160         * OutputStreamとエンコードより PrintWriterオブジェクトを作成します。
    161         *
    162         * @og.rev 5.5.2.0 (2012/05/01) 新規追加
    163         *
    164         * @param       os              利用するOutputStream
    165         * @param       encode  ファイルのエンコード
    166         *
    167         * @return      PrintWriterオブジェクト
    168         * @throws RuntimeException 何らかのエラーが発生した場合
    169         */
    170        public static PrintWriter getPrintWriter( final OutputStream os,final String encode ) {
    171                final PrintWriter writer ;
    172
    173                try {
    174                        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    175                                        os ,encode )));
    176                }
    177                catch( UnsupportedEncodingException ex ) {
    178                        String errMsg = "指定されたエンコーディングがサポートされていません。" + CR
    179                                                        + ex.getMessage() + CR
    180                                                        + "encode=[" + encode + "]" ;
    181                        throw new RuntimeException( errMsg,ex );
    182                }
    183                return writer ;
    184        }
    185
    186        /**
    187         * PrintWriter を継承した、JspWriterなどの Writer 用のクラスを定義します。
    188         *
    189         * 例えば、JspWriterなどの JSP/Servlet等のフレームワークで使用される
    190         * Writer では、flush や close 処理は、フレームワーク内で行われます。
    191         * その場合、通常のファイルと同じ用に、flush や close をアプリケーション側で
    192         * 行うと、内部処理的に不整合が発生したり、最悪の場合エラーになります。
    193         * このクラスは、NonFlushPrintWriter クラスのオブジェクトを返します。
    194         * これは、通常の、new PrintWriter( Writer ) で、求めるのと、ほとんど同様の
    195         * 処理を行いますが、close() と flush() メソッドが呼ばれても、何もしません。
    196         *
    197         * @param       writer  出力するWriteオブジェクト(NonFlushPrintWriterクラス)
    198         *
    199         * @return      PrintWriterオブジェクト
    200         */
    201        public static PrintWriter getNonFlushPrintWriter( final Writer writer ) {
    202                return new NonFlushPrintWriter( writer );
    203        }
    204
    205        /**
    206         * Fileオブジェクトとエンコードより BufferedReaderオブジェクトを作成します。
    207         *
    208         * @param       file    入力するファイルオブジェクト
    209         * @param       encode  ファイルのエンコード
    210         *
    211         * @return      BufferedReaderオブジェクト
    212         * @throws RuntimeException 何らかのエラーが発生した場合
    213         */
    214        public static BufferedReader getBufferedReader( final File file,final String encode ) {
    215                final BufferedReader reader ;
    216
    217                try {
    218                        reader = new BufferedReader(new InputStreamReader(
    219                                                        new FileInputStream( file ) ,encode ));
    220                }
    221                catch( UnsupportedEncodingException ex ) {
    222                        String errMsg = "指定されたエンコーディングがサポートされていません。" + CR
    223                                                        + ex.getMessage() + CR
    224                                                        + "FIle=[" + file + " , encode=[" + encode + "]" ;
    225                        throw new RuntimeException( errMsg,ex );
    226                }
    227                catch( FileNotFoundException ex ) {
    228                        String errMsg = "ファイル名がオープン出来ませんでした。" + CR
    229                                                        + ex.getMessage() + CR
    230                                                        + "FIle=[" + file + " , encode=[" + encode + "]" ;
    231                        throw new RuntimeException( errMsg,ex );
    232                }
    233
    234                return reader ;
    235        }
    236
    237        /**
    238         * 指定のファイル名が、実際に存在しているかどうかをチェックします。
    239         * 存在しない場合は、２秒毎に、３回確認します。
    240         * それでも存在しない場合は、エラーを返します。
    241         * return されるFileオブジェクトは、正規の形式(CanonicalFile)です。
    242         *
    243         * @param       dir                     フォルダ名
    244         * @param       filename        ファイル名
    245         *
    246         * @return      存在チェック(なければ null/あれば、CanonicalFile)
    247         */
    248        public static File checkFile( final String dir, final String filename ) {
    249                return checkFile( dir,filename,3 );
    250        }
    251
    252        /**
    253         * 指定のファイル名が、実際に存在しているかどうかをチェックします。
    254         * 存在しない場合は、２秒毎に、指定の回数分確認します。
    255         * それでも存在しない場合は、エラーを返します。
    256         * return されるFileオブジェクトは、正規の形式(CanonicalFile)です。
    257         *
    258         * @param       dir                     フォルダ名
    259         * @param       filename        ファイル名
    260         * @param       count           回数指定
    261         *
    262         * @return      存在チェック(なければ null/あれば、CanonicalFile)
    263         */
    264        public static File checkFile( final String dir, final String filename,final int count ) {
    265                File file = null;
    266
    267                int cnt = count;
    268                while( cnt > 0 ) {
    269                        file = new File( dir,filename );
    270                        if( file.exists() ) { break; }
    271                        else {
    272                                if( cnt == 1 ) { return null; }         // 残り１回の場合は、２秒待機せずに即抜ける。
    273                                try { Thread.sleep( 2000 );     }       // ２秒待機
    274                                catch ( InterruptedException ex ) {
    275                                        System.out.println( "InterruptedException" );
    276                                }
    277                                System.out.println();
    278                                System.out.print( "CHECK File Error! CNT=" + cnt );
    279                                System.out.print( " File=" + file.getAbsolutePath() );
    280                        }
    281                        cnt--;
    282                }
    283
    284                // ファイルの正式パス名の取得
    285                try {
    286                        return file.getCanonicalFile() ;
    287                }
    288                catch( IOException ex ) {
    289                        String errMsg = "ファイルの正式パス名が取得できません。[" + file.getAbsolutePath() + "]";
    290                        throw new RuntimeException( errMsg,ex );
    291                }
    292        }
    293
    294        /**
    295         * ファイルのバイナリコピーを行います。
    296         *
    297         * copy( File,File,false ) を呼び出します。
    298         *
    299         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
    300         *
    301         * @param       fromFile        コピー元ファイル名
    302         * @param       toFile          コピー先ファイル名
    303         *
    304         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
    305         * @see         #copy( File,File,boolean )
    306         */
    307        public static boolean copy( final String fromFile,final String toFile ) {
    308                return copy( new File( fromFile ), new File( toFile ), false );
    309        }
    310
    311        /**
    312         * ファイルのバイナリコピーを行います。
    313         *
    314         * copy( File,File,boolean ) を呼び出します。
    315         * 第３引数の、keepTimeStamp=true で、コピー元のファイルのタイムスタンプを、
    316         * コピー先にもセットします。
    317         *
    318         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
    319         *
    320         * @param       fromFile        コピー元ファイル名
    321         * @param       toFile          コピー先ファイル名
    322         * @param       keepTimeStamp   タイムスタンプ維持[true/false]
    323         *
    324         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
    325         * @see         #copy( File,File,boolean )
    326         */
    327        public static boolean copy( final String fromFile,final String toFile,final boolean keepTimeStamp ) {
    328                return copy( new File( fromFile ), new File( toFile ), keepTimeStamp );
    329        }
    330
    331        /**
    332         * ファイルのバイナリコピーを行います。
    333         *
    334         * copy( File,File,false ) を呼び出します。
    335         *
    336         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
    337         *
    338         * @param       fromFile        コピー元ファイル
    339         * @param       toFile          コピー先ファイル
    340         *
    341         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
    342         * @see         #copy( File,File,boolean )
    343         */
    344        public static boolean copy( final File fromFile,final File toFile ) {
    345                return copy( fromFile, toFile, false );
    346        }
    347
    348        /**
    349         * ファイルのバイナリコピーを行います。
    350         *
    351         * 第３引数の、keepTimeStamp=true で、コピー元のファイルのタイムスタンプを、
    352         * コピー先にもセットします。
    353         * toFile が、ディレクトリの場合は、fromFile のファイル名をそのままコピーします。
    354         * fromFile がディレクトリの場合は、エラーにします。
    355         * copyDirectry( File,Fileboolean )を使用してください。(自動処理はしていません)
    356         *
    357         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
    358         * @og.rev 5.6.5.2 (2013/06/21) ByteBufferを利用した方式から、transferTo を使用する方式に変更
    359         * @og.rev 5.7.1.2 (2013/12/20) copy先(toFile)のフォルダが存在しなければ、作成します。
    360         *
    361         * @param       fromFile        コピー元ファイル
    362         * @param       toFile          コピー先ファイル
    363         * @param       keepTimeStamp タイムスタンプ維持[true/false]
    364         *
    365         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
    366         * @see         #copyDirectry( File,File,boolean )
    367         */
    368        public static boolean copy( final File fromFile,final File toFile,final boolean keepTimeStamp ) {
    369                FileInputStream  inFile  = null;
    370                FileOutputStream outFile = null;
    371                FileChannel  fin  = null;
    372                FileChannel  fout = null;
    373
    374                File tempToFile = toFile ;
    375                try {
    376                        // fromFileが、ディレクトリの場合は、エラー
    377                        if( fromFile.isDirectory() ) {
    378                                System.err.println( fromFile + " がディレクトリのため、処理できません。" );
    379                                return false;
    380                        }
    381                        // toFileが、ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。
    382                        if( toFile.isDirectory() ) {
    383                                tempToFile = new File( toFile,fromFile.getName() );
    384                        }
    385
    386                        // 5.7.1.2 (2013/12/20) copy先(toFile)のフォルダが存在しなければ、作成します。
    387                        File parent = tempToFile.getParentFile();
    388                        if( !parent.exists() && !parent.mkdirs() ) {
    389                                // ディレクトリを作成する
    390                                System.err.println( parent + " の ディレクトリ作成に失敗しました。" );
    391                                return false;
    392                        }
    393
    394                        inFile  = new FileInputStream( fromFile );
    395                        outFile = new FileOutputStream( tempToFile );
    396
    397                        fin  = inFile.getChannel();
    398                        fout = outFile.getChannel();
    399
    400                        // 5.6.5.2 (2013/06/21) ByteBufferを利用した方式から、transferTo を使用する方式に変更
    401//                      ByteBuffer buffer = ByteBuffer.allocateDirect( BUFSIZE );
    402//                      while ( (fin.read(buffer) != -1) || buffer.position() > 0) {
    403//                              buffer.flip();
    404//                              fout.write( buffer );
    405//                              buffer.compact();
    406//                      }
    407
    408                        fin.transferTo(0, fin.size(), fout );
    409
    410                }
    411                catch ( IOException ex ) {
    412                        System.out.println(ex.getMessage());
    413                        return false;
    414                }
    415                finally {
    416                        Closer.ioClose( inFile  ) ;
    417                        Closer.ioClose( outFile );
    418                        Closer.ioClose( fin  ) ;
    419                        Closer.ioClose( fout );
    420                }
    421
    422                if( keepTimeStamp ) {
    423                        return tempToFile.setLastModified( fromFile.lastModified() );
    424                }
    425
    426                return true;
    427        }
    428
    429        /**
    430         * ファイルのバイナリコピーを行います。
    431         *
    432         * このファイルコピーは、バイナリファイルの 改行コードを
    433         * CR+LF に統一します。また、UTF-8 の BOM(0xef,0xbb,0xbf) があれば、
    434         * 取り除きます。
    435         *
    436         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
    437         *
    438         * @param       fromFile        コピー元ファイル
    439         * @param       toFile          コピー先ファイル
    440         *
    441         * @return      バイナリコピーが正常に終了したかどうか[true:成功/false:失敗]
    442         */
    443        public static boolean changeCrLfcopy( final File fromFile,final File toFile ) {
    444                BufferedInputStream     fromStream = null;
    445                BufferedOutputStream    toStream   = null;
    446                File tempToFile = toFile ;
    447                try {
    448                        // ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。
    449                        if( toFile.isDirectory() ) {
    450                                tempToFile = new File( toFile,fromFile.getName() );
    451                        }
    452                        fromStream = new BufferedInputStream( new FileInputStream( fromFile ) );
    453                        toStream   = new BufferedOutputStream( new FileOutputStream( tempToFile ) );
    454
    455                        byte[] buf = new byte[BUFSIZE];
    456                        int len ;
    457                        // 4.2.3.0 (2008/05/26) changeCrLf 属性対応
    458
    459                        boolean bomCheck = true;        // 最初の一回だけ、ＢＯＭチェックを行う。
    460                        byte    bt = (byte)0x00;        // バッファの最後と最初の比較時に使用
    461                        while( (len = fromStream.read(buf,0,BUFSIZE)) != -1 ) {
    462                                int st = 0;
    463                                if( bomCheck && len >= 3 &&
    464                                        buf[0] == (byte)0xef &&
    465                                        buf[1] == (byte)0xbb &&
    466                                        buf[2] == (byte)0xbf  ) {
    467                                                st = 3;
    468                                }
    469                                else {
    470                                        // バッファの最後が CR で、先頭が LF の場合、LF をパスします。
    471                                        if( bt == B_CR && buf[0] == B_LF ) {
    472                                                st = 1 ;
    473                                        }
    474                                }
    475                                bomCheck = false;
    476
    477                                for( int i=st;i<len;i++ ) {
    478                                        bt = buf[i] ;
    479                                        if( bt == B_CR || bt == B_LF ) {
    480                                                toStream.write( (int)B_CR );            // CR
    481                                                toStream.write( (int)B_LF );            // LF
    482                                                // CR+LF の場合
    483                                                if( bt == B_CR && i+1 < len && buf[i+1] == B_LF ) {
    484                                                        i++;
    485                                                        bt = buf[i] ;
    486                                                }
    487                                        }
    488                                        else {
    489                                                toStream.write( (int)bt );
    490                                        }
    491                                }
    492                        }
    493                        // 最後が改行コードでなければ、改行コードを追加します。
    494                        // テキストコピーとの互換性のため
    495                        if( bt != B_CR && bt != B_LF ) {
    496                                toStream.write( (int)B_CR );            // CR
    497                                toStream.write( (int)B_LF );            // LF
    498                        }
    499                }
    500                catch ( IOException ex ) {
    501                        System.out.println(ex.getMessage());
    502                        return false;
    503                }
    504                finally {
    505                        Closer.ioClose( fromStream ) ;
    506                        Closer.ioClose( toStream ) ;
    507                }
    508
    509                return true;
    510        }
    511
    512        /**
    513         * 入出力ストリーム間でデータの転送を行います。
    514         *
    515         * ここでは、すでに作成されたストリームに基づき、データの入出力を行います。
    516         * よって、先にフォルダ作成や、存在チェック、ファイルの削除などの必要な処理は
    517         * 済まして置いてください。
    518         * また、このメソッド内で、ストリームのクロース処理は行っていません。
    519         *
    520         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
    521         *
    522         * @param       input   入力ストリーム
    523         * @param       output  出力ストリーム
    524         *
    525         * @return      データ転送が正常に終了したかどうか[true:成功/false:失敗]
    526         */
    527        public static boolean copy( final InputStream input,final OutputStream output ) {
    528                if( input == null ) {
    529                        System.err.println( "入力ストリームが 作成されていません。" );
    530                        return false;
    531                }
    532
    533                if( output == null ) {
    534                        System.err.println( "出力ストリームが 作成されていません。" );
    535                        return false;
    536                }
    537
    538                try {
    539                        byte[] buf = new byte[BUFSIZE];
    540                        int len;
    541                        while((len = input.read(buf)) != -1) {
    542                                output.write(buf, 0, len);
    543                        }
    544                }
    545                catch ( IOException ex ) {
    546                        System.out.println( ex.getMessage() );
    547                        return false;
    548                }
    549        //      finally {
    550        //              Closer.ioClose( input );
    551        //              Closer.ioClose( output );
    552        //      }
    553                return true ;
    554        }
    555
    556        /**
    557         * 再帰処理でディレクトリのコピーを行います。
    558         *
    559         * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。
    560         *
    561         * @og.rev 4.3.0.0 (2008/07/24) 追加
    562         * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。
    563         *
    564         * @param       fromDir コピー元ディレクトリ名
    565         * @param       toDir   コピー先ディレクトリ名
    566         *
    567         * @return      ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗]
    568         */
    569        public static boolean copyDirectry( final String fromDir, final String toDir ) {
    570                return copyDirectry( new File( fromDir ), new File( toDir ),false );
    571        }
    572
    573        /**
    574         * 再帰処理でディレクトリをコピーします。
    575         *
    576         * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。
    577         *
    578         * @og.rev 4.3.0.0 (2008/07/24) 追加
    579         * @og.rev 5.1.6.0 (2010/05/01) 内部処理を若干変更します。
    580         *
    581         * @param       fromDir   コピー元ディレクトリ
    582         * @param       toDir     コピー先ディレクトリ
    583         *
    584         * @return      ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗]
    585         */
    586        public static boolean copyDirectry( final File fromDir, final File toDir ) {
    587                return copyDirectry( fromDir, toDir, false );
    588        }
    589
    590        /**
    591         * 再帰処理でディレクトリをコピーします。
    592         *
    593         * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。
    594         *
    595         * @og.rev 4.3.0.0 (2008/07/24) 追加
    596         * @og.rev 5.1.6.0 (2010/05/01) 内部処理を若干変更します。
    597         * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。
    598         *
    599         * @param       fromDir コピー元ディレクトリ
    600         * @param       toDir   コピー先ディレクトリ
    601         * @param       keepTimeStamp タイムスタンプ維持[true/false]
    602         *
    603         * @return      ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗]
    604         */
    605        public static boolean copyDirectry( final File fromDir, final File toDir, final boolean keepTimeStamp ) {
    606                // コピー元がディレクトリでない場合はfalseを返す
    607                // 4.3.4.4 (2009/01/01)
    608                if( !fromDir.exists() || !fromDir.isDirectory() ) {
    609                        System.err.println( fromDir + " が ディレクトリでないか、存在しません。" );
    610                        return false;
    611                }
    612
    613                // 4.3.4.4 (2009/01/01) ディレクトリを作成する
    614                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
    615                if( !toDir.exists() && !toDir.mkdirs() ) {
    616                        System.err.println( toDir + " の ディレクトリ作成に失敗しました。" );
    617                        return false;
    618                }
    619
    620                // ディレクトリ内のファイルをすべて取得する
    621                File[] files = fromDir.listFiles();
    622
    623                // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラー
    624                if( files == null ) {
    625                        System.err.println( fromDir + " はアクセスできません。" );
    626                        return false;
    627                }
    628
    629                // ディレクトリ内のファイルに対しコピー処理を行う
    630                boolean flag = true;
    631                for( int i = 0; files.length>i; i++ ){
    632                        if( files[i].isDirectory() ){ // ディレクトリだった場合は再帰呼び出しを行う
    633                                flag = copyDirectry( files[i], new File( toDir, files[i].getName()),keepTimeStamp );
    634                        }
    635                        else{ // ファイルだった場合はファイルコピー処理を行う
    636                                flag = copy( files[i], new File( toDir, files[i].getName()),keepTimeStamp );
    637                        }
    638                        if( !flag ) { return false; }
    639                }
    640                return true;
    641        }
    642
    643        /**
    644         * 指定されたファイル及びディレクトを削除します。
    645         * ディレクトリの場合はサブフォルダ及びファイルも削除します。
    646         * １つでもファイルの削除に失敗した場合、その時点で処理を中断しfalseを返します。
    647         *
    648         * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。
    649         *
    650         * @param       file 削除ファイル/ディレクトリ
    651         *
    652         * @return      ファイル/ディレクトリの削除に終了したかどうか[true:成功/false:失敗]
    653         */
    654        public static boolean deleteFiles( final File file ) {
    655                if( file.exists() ) {
    656                        if( file.isDirectory() ) {
    657                                File[] list = file.listFiles();
    658
    659                                // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラー
    660                                if( list == null ) {
    661                                        System.err.println( file + " はアクセスできません。" );
    662                                        return false;
    663                                }
    664
    665                                for( int i=0; i<list.length; i++ ) {
    666                                        deleteFiles( list[i] );
    667                                }
    668                        }
    669                        if( !file.delete() ) { return false; }
    670                }
    671                return true;
    672        }
    673
    674        /**
    675         * 指定されたディレクトリを基点としたファイル名(パスを含む)の一覧を返します。
    676         *
    677         * @og.rev 4.3.6.6 (2009/05/15) 新規作成
    678         * @og.rev 5.4.3.2 (2012/01/06) 引数isCopy追加
    679         *
    680         * @param dir 基点となるディレクトリ
    681         * @param sort ファイル名でソートするか
    682         * @param list ファイル名一覧を格納するList
    683         * @param isCopy コピー中ファイルを除外するか [true:含む/false:除外]
    684         */
    685        public static void getFileList( final File dir, final boolean sort, final List<String> list, boolean isCopy ) {
    686                if( list == null ) { return; }
    687                if( dir.isFile() ) {
    688                        // コピー中判定はrenameで行う
    689                        if( !isCopy && !dir.renameTo( dir ) ){
    690                                return;
    691                        }
    692                        else{
    693                                list.add( dir.getAbsolutePath() );
    694                        }
    695                }
    696                else if( dir.isDirectory() ) {
    697                        File[] files = dir.listFiles();
    698                        for( int i=0; i<files.length; i++ ) {
    699                                getFileList( files[i], sort, list, isCopy );
    700                        }
    701                }
    702                if( sort ) {
    703                        Collections.sort( list );
    704                }
    705        }
    706
    707        /**
    708         * 指定されたディレクトリを基点としたファイル名(パスを含む)の一覧を返します。
    709         * 互換性のため、コピー中ファイルも含みます。
    710         *
    711         * @og.rev 5.4.3.2 (2012/01/06) コピー中対応のため引数４つを作成する
    712         *
    713         * @param dir 基点となるディレクトリ
    714         * @param sort ファイル名でソートするか
    715         * @param list ファイル名一覧を格納するList
    716         */
    717        public static void getFileList( final File dir, final boolean sort, final List<String> list ) {
    718                        getFileList( dir, sort, list, true );
    719        }
    720
    721        /**
    722         * 指定されたファイル名(パスを含む)から、パスも拡張子もないファイル名を返します。
    723         *
    724         * @og.rev 5.6.1.2 (2013/02/22) 新規作成
    725         *
    726         * @param filename ファイル名(パスを含む)
    727         * @return パスも、拡張子もないファイル名
    728         */
    729        public static String getBaseName( final String filename ) {
    730
    731                if (filename == null) {
    732                        return null;
    733                }
    734
    735                // セパレータの位置を取得。
    736                int lastUnixPos    = filename.lastIndexOf(UNIX_SEPARATOR);
    737                int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
    738                int lastSepPos = Math.max( lastUnixPos , lastWindowsPos );
    739
    740                // 拡張子の位置を取得。
    741                int extPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
    742                if( lastSepPos > extPos ) { extPos = -1; }      // 念のため、最後のセパレータより前にある拡張子の区切り文字は無効。
    743
    744                if( extPos < 0 ) {
    745                        // SEPARATOR がなければ、lastSepPos + 1 = 0 となり、先頭から取得できる。
    746                        return filename.substring( lastSepPos + 1 );
    747                } else {
    748                        return filename.substring( lastSepPos + 1 , extPos );
    749                }
    750        }
    751
    752        /**
    753         * ファイルをリネームを行います。
    754         * 引数のuseBackup属性を true にすると、toFile が存在した場合、toFile の直下に "_backup" フォルダを
    755         * 作成して、toFile ＋ "_" ＋ (現在時刻のLONG値) ＋ "." ＋ (toFileの拡張子) に名前変更します。
    756         * useBackup属性を false にすると、toFile が存在した場合、toFile を削除します。
    757         *
    758         * @og.rev 5.7.1.2 (2013/12/20) 新規追加
    759         *
    760         * @param       fromFile        名前変更する元のファイル
    761         * @param       toFile          名前変更後のファイル
    762         * @param       useBackup       バックアップを作成するかどうか(true:作成する/false:作成しない)
    763         * @return      true:正常処理/false:異常処理
    764         */
    765        public static boolean renameTo( final File fromFile , final File toFile , final boolean useBackup ) {
    766                if( fromFile == null || toFile == null ) {
    767                        String errMsg = "入力ファイルが null です。" ;
    768                        System.err.println( errMsg );
    769                        return false;
    770                }
    771
    772                // 変更先のファイルが存在した場合の処理。
    773                if( toFile.exists() ) {
    774                        // バックアップ作成する場合
    775                        if( useBackup ) {
    776                                File parent = toFile.getParentFile();                   // バックアップすべきファイルのフォルダ
    777                                File backup = new File( parent , "_backup" );   // その直下に、"_backup" フォルダを作成
    778                                if( !backup.exists() && !backup.mkdirs() ) {
    779                                        String errMsg = "バックアップ処理でbackupフォルダの作成に失敗しました。[" + backup + "]";
    780                                        System.err.println( errMsg );
    781                                        return false;
    782                                }
    783                                // バックアップファイル名は、元のファイル名(拡張子含む) ＋ "_" + 現在時刻のlong値 + "." + 元のファイルの拡張子
    784                                String bkupName = toFile.getName();
    785                                File toFile2  = new File( parent,bkupName );    // オリジナルの toFile をrename するとまずいので、同名のFileオブジェクトを作成
    786
    787                                bkupName = bkupName + "_" + System.currentTimeMillis() + "."  + getExtension( bkupName ) ;
    788                                File bkupFile = new File( backup,bkupName );
    789
    790                                if( !toFile2.renameTo( bkupFile ) ) {
    791                                        String errMsg = "バックアップ処理でバックアップファイルをリネームできませんでした。" +CR
    792                                                                                 + "  [" + toFile + "] ⇒ [" + bkupFile + "]" ;
    793                                        System.err.println( errMsg );
    794                                        return false;
    795                                }
    796                        }
    797                        // バックアップ作成しない場合は、削除します。
    798                        else if( !toFile.delete() ) {
    799                                String errMsg = "既存のファイル[" + toFile + "]が削除できませんでした。";
    800                                System.err.println( errMsg );
    801                                return false;
    802                        }
    803                }
    804
    805                if( !fromFile.renameTo( toFile ) ) {
    806                        String errMsg = "所定のファイルをリネームできませんでした。" + CR
    807                                                                + "  [" + fromFile + "] ⇒ [" + toFile + "]" ;
    808                                System.err.println( errMsg );
    809                                return false;
    810                }
    811                return true;
    812        }
    813
    814        /**
    815         * ファイル名から 拡張子を取得します。
    816         *
    817         * 一番最後に見つかったピリオドから後ろを切り取って返します。
    818         * 拡張子の区切り文字(".")がなければ、空文字列を返します。
    819         *
    820         * @og.rev 5.7.1.2 (2013/12/20) UploadedFileからに移動。若干のロジック変更
    821         *
    822         * @param       fileName        ファイル名
    823         * @return      拡張子
    824         */
    825        public static String getExtension( final String fileName ) {
    826                int extPos = fileName.lastIndexOf( EXTENSION_SEPARATOR );
    827                if( extPos >= 0 ) {
    828                        return fileName.substring( extPos + 1 );
    829                }
    830                return "";
    831        }
    832
    833        /**
    834         * PrintWriter を継承した、System.out/System.err 用のクラスを定義します。
    835         *
    836         * 通常の、new PrintWriter( OutputStream ) で、求めるのと、ほとんど同様の
    837         * 処理を行います。
    838         * ただ、close() メソッドが呼ばれても、何もしません。
    839         *
    840         */
    841        private static final class NonClosePrintWriter extends PrintWriter {
    842                /**
    843                 * コンストラクター
    844                 *
    845                 * new PrintWriter( OutputStream ) を行います。
    846                 *
    847                 * @param out OutputStream
    848                 */
    849                public NonClosePrintWriter( final OutputStream out ) {
    850                        super( out );
    851                }
    852
    853                /**
    854                 * close() メソッドをオーバーライドします。
    855                 *
    856                 * 何もしません。
    857                 *
    858                 */
    859                public void close() {
    860                        // ここでは処理を行いません。
    861                }
    862        }
    863
    864        /**
    865         * PrintWriter を継承した、JspWriterなどの Writer 用のクラスを定義します。
    866         *
    867         * 例えば、JspWriterなどの JSP/Servlet等のフレームワークで使用される
    868         * Writer では、flush や close 処理は、フレームワーク内で行われます。
    869         * その場合、通常のファイルと同じ用に、flush や close をアプリケーション側で
    870         * 行うと、内部処理的に不整合が発生したり、最悪の場合エラーになります。
    871         * このクラスは、単に、通常の、new PrintWriter( Writer ) で、求めるのと、
    872         * ほとんど同様の処理を行います。
    873         * ただ、close() と flush() メソッドが呼ばれても、何もしません。
    874         *
    875         */
    876        private static final class NonFlushPrintWriter extends PrintWriter {
    877                /**
    878                 * コンストラクター
    879                 *
    880                 * new PrintWriter( Writer ) を行います。
    881                 *
    882                 * @param writer Writer
    883                 */
    884                public NonFlushPrintWriter( final Writer writer ) {
    885                        super( writer );
    886                }
    887
    888                /**
    889                 * close() メソッドをオーバーライドします。
    890                 *
    891                 * 何もしません。
    892                 *
    893                 */
    894                public void close() {
    895                        // ここでは処理を行いません。
    896                }
    897
    898                /**
    899                 * flush() メソッドをオーバーライドします。
    900                 *
    901                 * 何もしません。
    902                 *
    903                 */
    904                public void flush() {
    905                        // ここでは処理を行いません。
    906                }
    907        }
    908
    909        /**
    910         * ファイルをコピーします。
    911         *
    912         * 引数に &lt;file1&gt; &lt;file2&gt; [&lt;encode1&gt; &lt;encode2&gt;] を指定します。
    913         * file1 を読み込み、file2 にコピーします。コピー前に、file2 は、file2_backup にコピーします。
    914         * file1 が、ディレクトリの場合は、ディレクトリごとコピーします。
    915         * encode1、encode2 を指定すると、エンコード変換しながらコピーになります。
    916         * この場合は、ファイル同士のコピーのみになります。
    917         *
    918         * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。
    919         * @og.rev 5.1.6.0 (2010/05/01) 引数の並び順、処理を変更します。
    920         *
    921         * @param       args 引数配列  file1 file2 [encode1 encode2]
    922         * @throws Throwable なんらかのエラーが発生した場合。
    923         */
    924        public static void main( final String[] args ) throws Throwable {
    925                if( args.length != 2 && args.length != 4 ) {
    926                        LogWriter.log("Usage: java org.opengion.fukurou.util.FileUtil <file1> <file2> [<encode1> <encode2>]" );
    927                        return ;
    928                }
    929
    930                File file1 = new File( args[0] );
    931                File file2 = new File( args[1] );
    932
    933                File tempFile = new File( args[1] + "_backup" );
    934
    935                if( args.length < 3 ) {
    936                        if( file1.isDirectory() ) {
    937                                FileUtil.copyDirectry( file1, file2, true );
    938                        }
    939                        else {
    940                                FileUtil.copy( file2,tempFile );
    941                                FileUtil.copy( file1,file2, true );
    942                        }
    943                }
    944                else {
    945                        String encode1 = args[2];
    946                        String encode2 = args[3];
    947
    948                        FileUtil.copy( file2,tempFile );
    949
    950                        BufferedReader reader = FileUtil.getBufferedReader( file1 ,encode1 );
    951                        PrintWriter    writer = FileUtil.getPrintWriter(    file2 ,encode2 );
    952
    953                        try {
    954                                String line1;
    955                                while((line1 = reader.readLine()) != null) {
    956                                        writer.println( line1 );
    957                                }
    958                        }
    959                        finally {
    960                                Closer.ioClose( reader ) ;
    961                                Closer.ioClose( writer ) ;
    962                        }
    963                }
    964        }
    965}