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