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