2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.intellij
.openapi
.util
.io
;
19 import com
.intellij
.CommonBundle
;
20 import com
.intellij
.Patches
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.util
.ShutDownTracker
;
23 import com
.intellij
.openapi
.util
.SystemInfo
;
24 import com
.intellij
.openapi
.util
.text
.StringUtil
;
25 import com
.intellij
.util
.Processor
;
26 import org
.intellij
.lang
.annotations
.RegExp
;
27 import org
.jetbrains
.annotations
.NonNls
;
28 import org
.jetbrains
.annotations
.NotNull
;
29 import org
.jetbrains
.annotations
.Nullable
;
32 import java
.lang
.reflect
.InvocationTargetException
;
33 import java
.lang
.reflect
.Method
;
34 import java
.nio
.channels
.FileChannel
;
36 import java
.util
.regex
.Pattern
;
38 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
39 public class FileUtil
{
40 public static final int MEGABYTE
= 1024 * 1024;
42 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.util.io.FileUtil");
43 private static final ThreadLocal
<byte[]> BUFFER
= new ThreadLocal
<byte[]>() {
44 protected byte[] initialValue() {
45 return new byte[1024 * 20];
48 //private static final byte[] BUFFER = new byte[1024 * 20];
51 public static String
getRelativePath(File base
, File file
) {
52 if (base
== null || file
== null) return null;
54 if (!base
.isDirectory()) {
55 base
= base
.getParentFile();
56 if (base
== null) return null;
59 if (base
.equals(file
)) return ".";
61 final String filePath
= file
.getAbsolutePath();
62 String basePath
= base
.getAbsolutePath();
63 return getRelativePath(basePath
, filePath
, File
.separatorChar
);
66 public static String
getRelativePath(String basePath
, String filePath
, final char separator
) {
67 return getRelativePath(basePath
, filePath
, separator
, SystemInfo
.isFileSystemCaseSensitive
);
70 public static String
getRelativePath(String basePath
, String filePath
, final char separator
, final boolean caseSensitive
) {
71 if (!StringUtil
.endsWithChar(basePath
, separator
)) basePath
+= separator
;
74 int lastSeparatorIndex
= 0; // need this for cases like this: base="/temp/abcde/base" and file="/temp/ab"
75 String basePathToCompare
= caseSensitive ? basePath
: basePath
.toLowerCase();
76 String filePathToCompare
= caseSensitive ? filePath
: filePath
.toLowerCase();
77 while (len
< filePath
.length() && len
< basePath
.length() && filePathToCompare
.charAt(len
) == basePathToCompare
.charAt(len
)) {
78 if (basePath
.charAt(len
) == separator
) {
79 lastSeparatorIndex
= len
;
84 if (len
== 0) return null;
86 StringBuilder relativePath
= new StringBuilder();
87 for (int i
=len
; i
< basePath
.length(); i
++) {
88 if (basePath
.charAt(i
) == separator
) {
89 relativePath
.append("..");
90 relativePath
.append(separator
);
93 relativePath
.append(filePath
.substring(lastSeparatorIndex
+ 1));
95 return relativePath
.toString();
99 * Check if the {@code ancestor} is an ancestor of {@code file}.
101 * @param ancestor the file
102 * @param file the file
103 * @param strict if {@code false} then this method returns {@code true} if {@code ancestor}
104 * and {@code file} are equal
105 * @return {@code true} if {@code ancestor} is parent of {@code file}; {@code false} otherwise
106 * @throws IOException this exception is never thrown and left here for backward compatibilty
108 public static boolean isAncestor(File ancestor
, File file
, boolean strict
) throws IOException
{
109 File parent
= strict ?
getParentFile(file
) : file
;
111 if (parent
== null) {
114 if (parent
.equals(ancestor
)) {
117 parent
= getParentFile(parent
);
122 * Get parent for the file. The method correctly
123 * processes "." and ".." in file names. The name
124 * remains relative if was relative before.
126 * @param file a file to analyze
127 * @return a parent or the null if the file has no parent.
130 public static File
getParentFile(final File file
) {
132 File parentFile
= file
;
134 parentFile
= parentFile
.getParentFile();
135 if (parentFile
== null) {
138 if (".".equals(parentFile
.getName())) {
141 if ("..".equals(parentFile
.getName())) {
154 public static char[] loadFileText(File file
) throws IOException
{
155 return loadFileText(file
, null);
159 public static char[] loadFileText(File file
, @NonNls String encoding
) throws IOException
{
160 InputStream stream
= new FileInputStream(file
);
161 Reader reader
= encoding
== null ?
new InputStreamReader(stream
) : new InputStreamReader(stream
, encoding
);
163 return loadText(reader
, (int)file
.length());
171 public static char[] loadText(Reader reader
, int length
) throws IOException
{
172 char[] chars
= new char[length
];
174 while (count
< chars
.length
) {
175 int n
= reader
.read(chars
, count
, chars
.length
- count
);
179 if (count
== chars
.length
){
183 char[] newChars
= new char[count
];
184 System
.arraycopy(chars
, 0, newChars
, 0, count
);
190 public static byte[] loadFileBytes(File file
) throws IOException
{
192 final InputStream stream
= new FileInputStream(file
);
194 final long len
= file
.length();
196 throw new IOException("File length reported negative, probably doesn't exist");
199 if (len
> 100 * MEGABYTE
) {
200 throw new FileTooBigException("Attempt to load '" + file
+ "' in memory buffer, file length is " + len
+ " bytes.");
203 bytes
= loadBytes(stream
, (int)len
);
212 public static byte[] loadBytes(InputStream stream
, int length
) throws IOException
{
213 byte[] bytes
= new byte[length
];
215 while(count
< length
) {
216 int n
= stream
.read(bytes
, count
, length
- count
);
224 public static byte[] loadBytes(InputStream stream
) throws IOException
{
225 ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
226 final byte[] bytes
= BUFFER
.get();
228 int n
= stream
.read(bytes
, 0, bytes
.length
);
230 buffer
.write(bytes
, 0, n
);
233 return buffer
.toByteArray();
237 public static String
loadTextAndClose(Reader reader
) throws IOException
{
239 return new String(adaptiveLoadText(reader
));
247 public static char[] adaptiveLoadText(Reader reader
) throws IOException
{
248 char[] chars
= new char[4096];
249 List
<char[]> buffers
= null;
253 int n
= reader
.read(chars
, count
, chars
.length
-count
);
256 if (total
> 1024*1024*10) throw new FileTooBigException("File too big "+reader
);
258 if (count
== chars
.length
) {
259 if (buffers
== null) {
260 buffers
= new ArrayList
<char[]>();
263 int newLength
= Math
.min(1024*1024, chars
.length
* 2);
264 chars
= new char[newLength
];
268 char[] result
= new char[total
];
269 if (buffers
!= null) {
270 for (char[] buffer
: buffers
) {
271 System
.arraycopy(buffer
, 0, result
, result
.length
- total
, buffer
.length
);
272 total
-= buffer
.length
;
275 System
.arraycopy(chars
, 0, result
, result
.length
- total
, total
);
280 public static byte[] adaptiveLoadBytes(InputStream stream
) throws IOException
{
281 byte[] bytes
= new byte[4096];
282 List
<byte[]> buffers
= null;
286 int n
= stream
.read(bytes
, count
, bytes
.length
-count
);
289 if (total
> 1024*1024*10) throw new FileTooBigException("File too big "+stream
);
291 if (count
== bytes
.length
) {
292 if (buffers
== null) {
293 buffers
= new ArrayList
<byte[]>();
296 int newLength
= Math
.min(1024*1024, bytes
.length
* 2);
297 bytes
= new byte[newLength
];
301 byte[] result
= new byte[total
];
302 if (buffers
!= null) {
303 for (byte[] buffer
: buffers
) {
304 System
.arraycopy(buffer
, 0, result
, result
.length
- total
, buffer
.length
);
305 total
-= buffer
.length
;
308 System
.arraycopy(bytes
, 0, result
, result
.length
- total
, total
);
312 public static File
createTempDirectory(@NonNls String prefix
, @NonNls String suffix
) throws IOException
{
313 File file
= doCreateTempFile(prefix
, suffix
);
319 public static File
createTempFile(@NonNls final File dir
, @NonNls String prefix
, @NonNls String suffix
, final boolean create
) throws IOException
{
320 File file
= doCreateTempFile(prefix
, suffix
, dir
);
323 file
.createNewFile();
328 public static File
createTempFile(@NonNls String prefix
, @NonNls String suffix
) throws IOException
{
329 File file
= doCreateTempFile(prefix
, suffix
);
331 file
.createNewFile();
335 private static File
doCreateTempFile(String prefix
, String suffix
) throws IOException
{
336 return doCreateTempFile(prefix
, suffix
, new File(getTempDirectory()));
339 private static File
doCreateTempFile(String prefix
, String suffix
, final File dir
) throws IOException
{
340 if (prefix
.length() < 3) {
341 prefix
= (prefix
+ "___").substring(0, 3);
344 int exceptionsCount
= 0;
347 return File
.createTempFile(prefix
, suffix
, dir
).getCanonicalFile();
349 catch(IOException e
){ // Win32 createFileExclusively access denied
350 if (++exceptionsCount
>= 100) {
357 public static String
getTempDirectory() {
358 return System
.getProperty("java.io.tmpdir");
361 public static void asyncDelete(@NotNull File file
) {
362 final File tempFile
= renameToTempFileOrDelete(file
);
363 if (tempFile
== null) {
366 startDeletionThread(tempFile
);
369 public static void asyncDelete(@NotNull Collection
<File
> files
) {
370 List
<File
> tempFiles
= new ArrayList
<File
>();
371 for (File file
: files
) {
372 final File tempFile
= renameToTempFileOrDelete(file
);
373 if (tempFile
!= null) {
374 tempFiles
.add(tempFile
);
377 if (!tempFiles
.isEmpty()) {
378 startDeletionThread(tempFiles
.toArray(new File
[tempFiles
.size()]));
382 private static void startDeletionThread(@NotNull final File
... tempFiles
) {
383 final Runnable deleteFilesTask
= new Runnable() {
385 final Thread currentThread
= Thread
.currentThread();
386 currentThread
.setPriority(Thread
.MIN_PRIORITY
);
387 ShutDownTracker
.getInstance().registerStopperThread(currentThread
);
389 for (File tempFile
: tempFiles
) {
394 ShutDownTracker
.getInstance().unregisterStopperThread(currentThread
);
395 currentThread
.setPriority(Thread
.NORM_PRIORITY
);
401 // Attempt to execute on pooled thread
402 final Class
<?
> aClass
= Class
.forName("com.intellij.openapi.application.ApplicationManager");
403 final Method getApplicationMethod
= aClass
.getMethod("getApplication");
404 final Object application
= getApplicationMethod
.invoke(null);
405 final Method executeOnPooledThreadMethod
= application
.getClass().getMethod("executeOnPooledThread", Runnable
.class);
406 executeOnPooledThreadMethod
.invoke(application
, deleteFilesTask
);
408 catch (Exception e
) {
409 //noinspection HardCodedStringLiteral
410 Thread t
= new Thread(deleteFilesTask
, "File deletion thread");
415 private static File
renameToTempFileOrDelete(File file
) {
416 final File tempDir
= new File(getTempDirectory());
417 boolean isSameDrive
= true;
418 if (SystemInfo
.isWindows
) {
419 String tempDirDrive
= tempDir
.getAbsolutePath().substring(0, 2);
420 String fileDrive
= file
.getAbsolutePath().substring(0, 2);
421 isSameDrive
= tempDirDrive
.equalsIgnoreCase(fileDrive
);
425 // the optimization is reasonable only if destination dir is located on the same drive
426 final String originalFileName
= file
.getName();
427 File tempFile
= getTempFile(originalFileName
, tempDir
);
428 if (file
.renameTo(tempFile
)) {
438 private static File
getTempFile(String originalFileName
, File parent
) {
439 int randomSuffix
= (int)(System
.currentTimeMillis() % 1000);
440 for (int i
= randomSuffix
; ; i
++) {
441 @NonNls String name
= "___" + originalFileName
+ i
+ ".__del__";
442 File tempFile
= new File(parent
, name
);
443 if (!tempFile
.exists()) return tempFile
;
447 public static boolean delete(File file
){
448 File
[] files
= file
.listFiles();
450 for (File file1
: files
) {
451 if (!delete(file1
)) return false;
455 for (int i
= 0; i
< 10; i
++){
456 if (file
.delete() || !file
.exists()) return true;
460 catch (InterruptedException ignored
) {
467 public static boolean createParentDirs(File file
) {
468 if (!file
.exists()) {
469 String parentDirPath
= file
.getParent();
470 if (parentDirPath
!= null) {
471 final File parentFile
= new File(parentDirPath
);
472 return parentFile
.exists() && parentFile
.isDirectory() || parentFile
.mkdirs();
478 public static boolean createIfDoesntExist(File file
) {
479 if (file
.exists()) return true;
481 if (!createParentDirs(file
)) return false;
483 OutputStream s
= new FileOutputStream(file
);
487 catch (IOException e
) {
492 public static boolean ensureCanCreateFile(File file
) {
493 if (file
.exists()) return file
.canWrite();
494 if (!createIfDoesntExist(file
)) return false;
498 public static void copy(File fromFile
, File toFile
) throws IOException
{
499 performCopy(fromFile
, toFile
, true);
502 public static void copyContent(File fromFile
, File toFile
) throws IOException
{
503 performCopy(fromFile
, toFile
, false);
506 private static void performCopy(File fromFile
, File toFile
, final boolean syncTimestamp
) throws IOException
{
507 FileOutputStream fos
;
509 fos
= new FileOutputStream(toFile
);
511 catch (FileNotFoundException e
) {
512 File parentFile
= toFile
.getParentFile();
513 if (parentFile
== null) {
514 final IOException ioException
= new IOException("parent file is null for " + toFile
.getPath());
515 ioException
.initCause(e
);
518 createParentDirs(toFile
);
519 fos
= new FileOutputStream(toFile
);
522 if (Patches
.FILE_CHANNEL_TRANSFER_BROKEN
) {
523 FileInputStream fis
= new FileInputStream(fromFile
);
533 FileChannel fromChannel
= new FileInputStream(fromFile
).getChannel();
534 FileChannel toChannel
= fos
.getChannel();
536 fromChannel
.transferTo(0, Long
.MAX_VALUE
, toChannel
);
545 toFile
.setLastModified(fromFile
.lastModified());
549 public static void copy(InputStream inputStream
, OutputStream outputStream
) throws IOException
{
550 final byte[] buffer
= BUFFER
.get();
552 int read
= inputStream
.read(buffer
);
554 outputStream
.write(buffer
, 0, read
);
558 public static void copy(InputStream inputStream
, int size
, OutputStream outputStream
) throws IOException
{
559 final byte[] buffer
= BUFFER
.get();
562 int read
= inputStream
.read(buffer
, 0, Math
.min(buffer
.length
, toRead
));
565 outputStream
.write(buffer
, 0, read
);
569 public static void copyDir(File fromDir
, File toDir
) throws IOException
{
570 copyDir(fromDir
, toDir
, true);
573 public static void copyDir(File fromDir
, File toDir
, boolean copySystemFiles
) throws IOException
{
575 if (isAncestor(fromDir
, toDir
, true)) {
576 LOG
.error(fromDir
.getAbsolutePath() + " is ancestor of " + toDir
+ ". Can't copy to itself.");
579 File
[] files
= fromDir
.listFiles();
580 if(files
== null) throw new IOException(CommonBundle
.message("exception.directory.is.invalid", fromDir
.getPath()));
581 if(!fromDir
.canRead()) throw new IOException(CommonBundle
.message("exception.directory.is.not.readable", fromDir
.getPath()));
582 for (File file
: files
) {
583 if (!copySystemFiles
&& file
.getName().startsWith(".")) {
586 if (file
.isDirectory()) {
587 copyDir(file
, new File(toDir
, file
.getName()), copySystemFiles
);
590 copy(file
, new File(toDir
, file
.getName()));
595 public static String
getNameWithoutExtension(File file
) {
596 return getNameWithoutExtension(file
.getName());
599 public static String
getNameWithoutExtension(String name
) {
600 int i
= name
.lastIndexOf('.');
602 name
= name
.substring(0, i
);
607 public static String
createSequentFileName(File aParentFolder
, @NonNls String aFilePrefix
, String aExtension
) {
608 return findSequentNonexistentFile(aParentFolder
, aFilePrefix
, aExtension
).getName();
611 public static File
findSequentNonexistentFile(final File aParentFolder
, @NonNls final String aFilePrefix
, final String aExtension
) {
613 String ext
= 0 == aExtension
.length() ?
"" : "." + aExtension
;
615 File candidate
= new File(aParentFolder
, aFilePrefix
+ ext
);
616 while (candidate
.exists()) {
618 candidate
= new File(aParentFolder
, aFilePrefix
+ Integer
.toString(postfix
) + ext
);
623 public static String
toSystemDependentName(@NonNls @NotNull String aFileName
) {
624 return aFileName
.replace('/', File
.separatorChar
).replace('\\', File
.separatorChar
);
627 public static String
toSystemIndependentName(@NonNls @NotNull String aFileName
) {
628 return aFileName
.replace('\\', '/');
631 //TODO: does only %20 need to be unescaped?
632 public static String
unquote(String urlString
) {
633 urlString
= urlString
.replace('/', File
.separatorChar
);
634 urlString
= StringUtil
.replace(urlString
, "%20", " ");
635 return StringUtil
.replace(urlString
, "%21", "!");
638 public static boolean isFilePathAcceptable(File file
, @Nullable FileFilter fileFilter
) {
640 if (fileFilter
!= null && !fileFilter
.accept(file
)) return false;
641 file
= file
.getParentFile();
643 while (file
!= null);
647 public static void rename(final File source
, final File target
) throws IOException
{
648 if (source
.renameTo(target
)) return;
649 if (!source
.exists()) return;
651 copy(source
, target
);
655 public static boolean startsWith(@NonNls String path1
, @NonNls String path2
) {
656 return startsWith(path1
, path2
, SystemInfo
.isFileSystemCaseSensitive
);
659 public static boolean startsWith(final String path1
, final String path2
, final boolean caseSensitive
) {
660 final int length1
= path1
.length();
661 final int length2
= path2
.length();
662 if (length2
== 0) return true;
663 if (length2
> length1
) return false;
664 if (!path1
.regionMatches(!caseSensitive
, 0, path2
, 0, length2
)) return false;
665 if (length1
== length2
) return true;
666 char last2
= path2
.charAt(length2
- 1);
668 if (last2
== '/' || last2
== File
.separatorChar
) {
669 next1
= path1
.charAt(length2
-1);
672 next1
= path1
.charAt(length2
);
674 return next1
== '/' || next1
== File
.separatorChar
;
677 public static boolean pathsEqual(String path1
, String path2
) {
678 return SystemInfo
.isFileSystemCaseSensitive? path1
.equals(path2
) : path1
.equalsIgnoreCase(path2
);
681 public static int comparePaths(String path1
, String path2
) {
682 return SystemInfo
.isFileSystemCaseSensitive? path1
.compareTo(path2
) : path1
.compareToIgnoreCase(path2
);
685 public static int pathHashCode(String path
) {
686 return SystemInfo
.isFileSystemCaseSensitive? path
.hashCode() : path
.toLowerCase().hashCode();
690 public static String
getExtension(@NotNull String fileName
) {
691 int index
= fileName
.lastIndexOf('.');
692 if (index
< 0) return "";
693 return fileName
.substring(index
+ 1).toLowerCase();
697 public static String
resolveShortWindowsName(@NotNull final String path
) throws IOException
{
698 if (SystemInfo
.isWindows
) {
699 //todo: this resolves symlinks on Windows, but we'd rather not do it
700 return new File(path
.replace(File
.separatorChar
, '/')).getCanonicalPath();
705 public static void collectMatchedFiles(final File root
, final Pattern pattern
, final List
<File
> files
) {
706 collectMatchedFiles(root
, root
, pattern
, files
);
709 private static void collectMatchedFiles(final File absoluteRoot
, final File root
, final Pattern pattern
, final List
<File
> files
) {
710 final File
[] dirs
= root
.listFiles();
711 if (dirs
== null) return;
712 for (File dir
: dirs
) {
714 final String path
= toSystemIndependentName(getRelativePath(absoluteRoot
, dir
));
715 if (pattern
.matcher(path
).matches()) {
719 collectMatchedFiles(absoluteRoot
, dir
, pattern
, files
);
725 public static String
convertAntToRegexp(String antPattern
) {
726 return convertAntToRegexp(antPattern
, true);
730 * @param antPattern ant-style path pattern
731 * @return java regexp pattern.
732 * Note that no matter whether forward or backward slashes were used in the antPattern
733 * the returned regexp pattern will use forward slashes ('/') as file separators.
734 * Paths containing windows-style backslashes must be converted before matching against the resulting regexp
735 * @see com.intellij.openapi.util.io.FileUtil#toSystemIndependentName
738 public static String
convertAntToRegexp(String antPattern
, boolean ignoreStartingSlash
) {
739 final StringBuilder builder
= new StringBuilder(antPattern
.length());
740 int asteriskCount
= 0;
741 boolean recursive
= true;
742 final int start
= ignoreStartingSlash
&& (antPattern
.startsWith("/") || antPattern
.startsWith("\\")) ?
1 : 0;
743 for (int idx
= start
; idx
< antPattern
.length(); idx
++) {
744 final char ch
= antPattern
.charAt(idx
);
751 final boolean foundRecursivePattern
= recursive
&& asteriskCount
== 2 && (ch
== '/' || ch
== '\\');
752 final boolean asterisksFound
= asteriskCount
> 0;
755 recursive
= ch
== '/' || ch
== '\\';
757 if (foundRecursivePattern
) {
758 builder
.append("(?:[^/]+/)*?");
763 builder
.append("[^/]*?");
766 if (ch
== '(' || ch
== ')' || ch
== '[' || ch
== ']' || ch
== '^' || ch
== '$' || ch
== '.' || ch
== '{' || ch
== '}' || ch
== '+' || ch
== '|') {
767 // quote regexp-specific symbols
768 builder
.append('\\').append(ch
);
772 builder
.append("[^/]{1}");
782 // handle ant shorthand: mypackage/test/ is interpreted as if it were mypackage/test/**
783 final boolean isTrailingSlash
= builder
.length() > 0 && builder
.charAt(builder
.length() - 1) == '/';
784 if (asteriskCount
== 0 && isTrailingSlash
|| recursive
&& asteriskCount
== 2) {
785 if (isTrailingSlash
) {
786 builder
.setLength(builder
.length() - 1);
788 if (builder
.length() == 0) {
789 builder
.append(".*");
792 builder
.append("(?:$|/.+)");
795 else if (asteriskCount
> 0) {
796 builder
.append("[^/]*?");
798 return builder
.toString();
801 public static boolean moveDirWithContent(File fromDir
, File toDir
) {
802 if (!toDir
.exists()) return fromDir
.renameTo(toDir
);
804 File
[] files
= fromDir
.listFiles();
805 if (files
== null) return false;
807 boolean success
= true;
809 for (File fromFile
: files
) {
810 File toFile
= new File(toDir
, fromFile
.getName());
811 success
= success
&& fromFile
.renameTo(toFile
);
818 public static String
sanitizeFileName(String name
) {
819 StringBuilder result
= new StringBuilder();
821 for (int i
= 0; i
< name
.length(); i
++) {
822 final char ch
= name
.charAt(i
);
824 if (ch
> 0 && ch
< 255) {
825 if (Character
.isLetterOrDigit(ch
)) {
838 return result
.toString();
841 private static final Method IO_FILE_CAN_EXECUTE_METHOD
;
845 method
= File
.class.getDeclaredMethod("canExecute", boolean.class);
847 catch (NoSuchMethodException e
) {
850 IO_FILE_CAN_EXECUTE_METHOD
= method
;
853 public static boolean canCallCanExecute() {
854 return IO_FILE_CAN_EXECUTE_METHOD
!= null;
857 public static boolean canExecute(File file
) {
859 return ((Boolean
)IO_FILE_CAN_EXECUTE_METHOD
.invoke(file
));
861 catch (Exception e
) {
867 //File.setWritable() since java 6.0
868 private static final Method IO_FILE_SET_WRITABLE_METHOD
;
872 method
= File
.class.getDeclaredMethod("setWritable", boolean.class);
874 catch (NoSuchMethodException e
) {
877 IO_FILE_SET_WRITABLE_METHOD
= method
;
879 public static void setReadOnlyAttribute(String path
, boolean readOnlyStatus
) throws IOException
{
880 if (IO_FILE_SET_WRITABLE_METHOD
!= null) {
882 IO_FILE_SET_WRITABLE_METHOD
.invoke(new File(path
), !readOnlyStatus
);
885 catch (IllegalAccessException e
) {
888 catch (InvocationTargetException e
) {
893 if (SystemInfo
.isWindows
) {
894 process
= Runtime
.getRuntime().exec(new String
[]{"attrib", readOnlyStatus ?
"+r" : "-r", path
});
896 else { // UNIXes go here
897 process
= Runtime
.getRuntime().exec(new String
[]{"chmod", readOnlyStatus ?
"u-w" : "u+w", path
});
902 catch (InterruptedException ignored
) {
907 * The method File.setExecutalbe() (which is avaialable since java 6.0)
909 private static final Method IO_FILE_SET_EXECUTABLE_METHOD
;
913 method
= File
.class.getDeclaredMethod("setExecutable", boolean.class);
915 catch (NoSuchMethodException e
) {
918 IO_FILE_SET_EXECUTABLE_METHOD
= method
;
922 * Set executable attibute, it makes sense only on non-windows platforms.
924 * @param path the path to use
925 * @param executableFlag new value of executable attribute
926 * @throws IOException if there is a problem with setting the flag
928 public static void setExectuableAttribute(String path
, boolean executableFlag
) throws IOException
{
929 if (IO_FILE_SET_EXECUTABLE_METHOD
!= null) {
931 IO_FILE_SET_EXECUTABLE_METHOD
.invoke(new File(path
), executableFlag
);
934 catch (IllegalAccessException e
) {
937 catch (InvocationTargetException e
) {
941 if (!SystemInfo
.isWindows
) {
943 Process process
= Runtime
.getRuntime().exec(new String
[]{"chmod", executableFlag ?
"u+x" : "u-x", path
});
947 catch (InterruptedException ignored
) {
952 public static void writeToFile(final File file
, final byte[] text
) throws IOException
{
953 writeToFile(file
, text
, false);
956 public static void writeToFile(final File file
, final byte[] text
, boolean append
) throws IOException
{
957 createParentDirs(file
);
958 OutputStream stream
= new BufferedOutputStream(new FileOutputStream(file
, append
));
968 public static boolean processFilesRecursively(final File root
, final Processor
<File
> processor
) {
969 final LinkedList
<File
> queue
= new LinkedList
<File
>();
971 while (!queue
.isEmpty()) {
972 final File file
= queue
.removeFirst();
973 if (!processor
.process(file
)) return false;
974 if (file
.isDirectory()) {
975 final File
[] children
= file
.listFiles();
976 if (children
!= null) {
977 queue
.addAll(Arrays
.asList(children
));
985 public static File
findFirstThatExist(String
... paths
) {
986 for (String path
: paths
) {
987 if (!StringUtil
.isEmptyOrSpaces(path
)) {
988 File file
= new File(toSystemDependentName(path
));
989 if (file
.exists()) return file
;