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