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         * 存在しない場合は、2秒毎に、3回確認します。
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         * 存在しない場合は、2秒毎に、指定の回数分確認します。
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; }         // 残り1回の場合は、2秒待機せずに即抜ける。
286                                try { Thread.sleep( 2000 );     }       // 2秒待機
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         * 第3引数の、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         * 第3引数の、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;        // 最初の一回だけ、BOMチェックを行う。
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         * 1つでもファイルの削除に失敗した場合、その時点で処理を中断し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) コピー中対応のため引数4つを作成する
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.rev 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}