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 com
.intellij
.util
.io
.URLUtil
;
27 import org
.intellij
.lang
.annotations
.RegExp
;
28 import org
.jetbrains
.annotations
.NonNls
;
29 import org
.jetbrains
.annotations
.NotNull
;
30 import org
.jetbrains
.annotations
.Nullable
;
33 import java
.lang
.reflect
.InvocationTargetException
;
34 import java
.lang
.reflect
.Method
;
35 import java
.nio
.channels
.FileChannel
;
37 import java
.util
.regex
.Pattern
;
39 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
40 public class FileUtil
{
41 public static final int MEGABYTE
= 1024 * 1024;
43 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.util.io.FileUtil");
44 private static final ThreadLocal
<byte[]> BUFFER
= new ThreadLocal
<byte[]>() {
45 protected byte[] initialValue() {
46 return new byte[1024 * 20];
49 //private static final byte[] BUFFER = new byte[1024 * 20];
52 public static String
getRelativePath(File base
, File file
) {
53 if (base
== null || file
== null) return null;
55 if (!base
.isDirectory()) {
56 base
= base
.getParentFile();
57 if (base
== null) return null;
60 if (base
.equals(file
)) return ".";
62 final String filePath
= file
.getAbsolutePath();
63 String basePath
= base
.getAbsolutePath();
64 return getRelativePath(basePath
, filePath
, File
.separatorChar
);
67 public static String
getRelativePath(String basePath
, String filePath
, final char separator
) {
68 return getRelativePath(basePath
, filePath
, separator
, SystemInfo
.isFileSystemCaseSensitive
);
71 public static String
getRelativePath(String basePath
, String filePath
, final char separator
, final boolean caseSensitive
) {
72 if (!StringUtil
.endsWithChar(basePath
, separator
)) basePath
+= separator
;
75 int lastSeparatorIndex
= 0; // need this for cases like this: base="/temp/abcde/base" and file="/temp/ab"
76 String basePathToCompare
= caseSensitive ? basePath
: basePath
.toLowerCase();
77 String filePathToCompare
= caseSensitive ? filePath
: filePath
.toLowerCase();
78 while (len
< filePath
.length() && len
< basePath
.length() && filePathToCompare
.charAt(len
) == basePathToCompare
.charAt(len
)) {
79 if (basePath
.charAt(len
) == separator
) {
80 lastSeparatorIndex
= len
;
85 if (len
== 0) return null;
87 StringBuilder relativePath
= new StringBuilder();
88 for (int i
=len
; i
< basePath
.length(); i
++) {
89 if (basePath
.charAt(i
) == separator
) {
90 relativePath
.append("..");
91 relativePath
.append(separator
);
94 relativePath
.append(filePath
.substring(lastSeparatorIndex
+ 1));
96 return relativePath
.toString();
100 * Check if the {@code ancestor} is an ancestor of {@code file}.
102 * @param ancestor the file
103 * @param file the file
104 * @param strict if {@code false} then this method returns {@code true} if {@code ancestor}
105 * and {@code file} are equal
106 * @return {@code true} if {@code ancestor} is parent of {@code file}; {@code false} otherwise
107 * @throws IOException this exception is never thrown and left here for backward compatibilty
109 public static boolean isAncestor(File ancestor
, File file
, boolean strict
) throws IOException
{
110 File parent
= strict ?
getParentFile(file
) : file
;
112 if (parent
== null) {
115 if (parent
.equals(ancestor
)) {
118 parent
= getParentFile(parent
);
123 * Get parent for the file. The method correctly
124 * processes "." and ".." in file names. The name
125 * remains relative if was relative before.
127 * @param file a file to analyze
128 * @return a parent or the null if the file has no parent.
131 public static File
getParentFile(final File file
) {
133 File parentFile
= file
;
135 parentFile
= parentFile
.getParentFile();
136 if (parentFile
== null) {
139 if (".".equals(parentFile
.getName())) {
142 if ("..".equals(parentFile
.getName())) {
155 public static char[] loadFileText(File file
) throws IOException
{
156 return loadFileText(file
, null);
160 public static char[] loadFileText(File file
, @NonNls String encoding
) throws IOException
{
161 InputStream stream
= new FileInputStream(file
);
162 Reader reader
= encoding
== null ?
new InputStreamReader(stream
) : new InputStreamReader(stream
, encoding
);
164 return loadText(reader
, (int)file
.length());
172 public static char[] loadText(Reader reader
, int length
) throws IOException
{
173 char[] chars
= new char[length
];
175 while (count
< chars
.length
) {
176 int n
= reader
.read(chars
, count
, chars
.length
- count
);
180 if (count
== chars
.length
){
184 char[] newChars
= new char[count
];
185 System
.arraycopy(chars
, 0, newChars
, 0, count
);
191 public static byte[] loadFileBytes(File file
) throws IOException
{
193 final InputStream stream
= new FileInputStream(file
);
195 final long len
= file
.length();
197 throw new IOException("File length reported negative, probably doesn't exist");
200 if (len
> 100 * MEGABYTE
) {
201 throw new FileTooBigException("Attempt to load '" + file
+ "' in memory buffer, file length is " + len
+ " bytes.");
204 bytes
= loadBytes(stream
, (int)len
);
213 public static byte[] loadBytes(InputStream stream
, int length
) throws IOException
{
214 byte[] bytes
= new byte[length
];
216 while(count
< length
) {
217 int n
= stream
.read(bytes
, count
, length
- count
);
225 public static byte[] loadBytes(InputStream stream
) throws IOException
{
226 ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
227 final byte[] bytes
= BUFFER
.get();
229 int n
= stream
.read(bytes
, 0, bytes
.length
);
231 buffer
.write(bytes
, 0, n
);
234 return buffer
.toByteArray();
238 public static String
loadTextAndClose(Reader reader
) throws IOException
{
240 return new String(adaptiveLoadText(reader
));
248 public static char[] adaptiveLoadText(Reader reader
) throws IOException
{
249 char[] chars
= new char[4096];
250 List
<char[]> buffers
= null;
254 int n
= reader
.read(chars
, count
, chars
.length
-count
);
257 if (total
> 1024*1024*10) throw new FileTooBigException("File too big "+reader
);
259 if (count
== chars
.length
) {
260 if (buffers
== null) {
261 buffers
= new ArrayList
<char[]>();
264 int newLength
= Math
.min(1024*1024, chars
.length
* 2);
265 chars
= new char[newLength
];
269 char[] result
= new char[total
];
270 if (buffers
!= null) {
271 for (char[] buffer
: buffers
) {
272 System
.arraycopy(buffer
, 0, result
, result
.length
- total
, buffer
.length
);
273 total
-= buffer
.length
;
276 System
.arraycopy(chars
, 0, result
, result
.length
- total
, total
);
281 public static byte[] adaptiveLoadBytes(InputStream stream
) throws IOException
{
282 byte[] bytes
= new byte[4096];
283 List
<byte[]> buffers
= null;
287 int n
= stream
.read(bytes
, count
, bytes
.length
-count
);
290 if (total
> 1024*1024*10) throw new FileTooBigException("File too big "+stream
);
292 if (count
== bytes
.length
) {
293 if (buffers
== null) {
294 buffers
= new ArrayList
<byte[]>();
297 int newLength
= Math
.min(1024*1024, bytes
.length
* 2);
298 bytes
= new byte[newLength
];
302 byte[] result
= new byte[total
];
303 if (buffers
!= null) {
304 for (byte[] buffer
: buffers
) {
305 System
.arraycopy(buffer
, 0, result
, result
.length
- total
, buffer
.length
);
306 total
-= buffer
.length
;
309 System
.arraycopy(bytes
, 0, result
, result
.length
- total
, total
);
313 public static File
createTempDirectory(@NonNls String prefix
, @NonNls String suffix
) throws IOException
{
314 File file
= doCreateTempFile(prefix
, suffix
);
320 public static File
createTempFile(@NonNls final File dir
, @NonNls String prefix
, @NonNls String suffix
, final boolean create
) throws IOException
{
321 File file
= doCreateTempFile(prefix
, suffix
, dir
);
324 file
.createNewFile();
329 public static File
createTempFile(@NonNls String prefix
, @NonNls String suffix
) throws IOException
{
330 File file
= doCreateTempFile(prefix
, suffix
);
332 file
.createNewFile();
336 private static File
doCreateTempFile(String prefix
, String suffix
) throws IOException
{
337 return doCreateTempFile(prefix
, suffix
, new File(getTempDirectory()));
340 private static File
doCreateTempFile(String prefix
, String suffix
, final File dir
) throws IOException
{
341 if (prefix
.length() < 3) {
342 prefix
= (prefix
+ "___").substring(0, 3);
345 int exceptionsCount
= 0;
348 return File
.createTempFile(prefix
, suffix
, dir
).getCanonicalFile();
350 catch(IOException e
){ // Win32 createFileExclusively access denied
351 if (++exceptionsCount
>= 100) {
358 public static String
getTempDirectory() {
359 return System
.getProperty("java.io.tmpdir");
362 public static void asyncDelete(@NotNull File file
) {
363 final File tempFile
= renameToTempFileOrDelete(file
);
364 if (tempFile
== null) {
367 startDeletionThread(tempFile
);
370 public static void asyncDelete(@NotNull Collection
<File
> files
) {
371 List
<File
> tempFiles
= new ArrayList
<File
>();
372 for (File file
: files
) {
373 final File tempFile
= renameToTempFileOrDelete(file
);
374 if (tempFile
!= null) {
375 tempFiles
.add(tempFile
);
378 if (!tempFiles
.isEmpty()) {
379 startDeletionThread(tempFiles
.toArray(new File
[tempFiles
.size()]));
383 private static void startDeletionThread(@NotNull final File
... tempFiles
) {
384 final Runnable deleteFilesTask
= new Runnable() {
386 final Thread currentThread
= Thread
.currentThread();
387 currentThread
.setPriority(Thread
.MIN_PRIORITY
);
388 ShutDownTracker
.getInstance().registerStopperThread(currentThread
);
390 for (File tempFile
: tempFiles
) {
395 ShutDownTracker
.getInstance().unregisterStopperThread(currentThread
);
396 currentThread
.setPriority(Thread
.NORM_PRIORITY
);
402 // Attempt to execute on pooled thread
403 final Class
<?
> aClass
= Class
.forName("com.intellij.openapi.application.ApplicationManager");
404 final Method getApplicationMethod
= aClass
.getMethod("getApplication");
405 final Object application
= getApplicationMethod
.invoke(null);
406 final Method executeOnPooledThreadMethod
= application
.getClass().getMethod("executeOnPooledThread", Runnable
.class);
407 executeOnPooledThreadMethod
.invoke(application
, deleteFilesTask
);
409 catch (Exception e
) {
410 //noinspection HardCodedStringLiteral
411 Thread t
= new Thread(deleteFilesTask
, "File deletion thread");
416 private static File
renameToTempFileOrDelete(File file
) {
417 final File tempDir
= new File(getTempDirectory());
418 boolean isSameDrive
= true;
419 if (SystemInfo
.isWindows
) {
420 String tempDirDrive
= tempDir
.getAbsolutePath().substring(0, 2);
421 String fileDrive
= file
.getAbsolutePath().substring(0, 2);
422 isSameDrive
= tempDirDrive
.equalsIgnoreCase(fileDrive
);
426 // the optimization is reasonable only if destination dir is located on the same drive
427 final String originalFileName
= file
.getName();
428 File tempFile
= getTempFile(originalFileName
, tempDir
);
429 if (file
.renameTo(tempFile
)) {
439 private static File
getTempFile(String originalFileName
, File parent
) {
440 int randomSuffix
= (int)(System
.currentTimeMillis() % 1000);
441 for (int i
= randomSuffix
; ; i
++) {
442 @NonNls String name
= "___" + originalFileName
+ i
+ ".__del__";
443 File tempFile
= new File(parent
, name
);
444 if (!tempFile
.exists()) return tempFile
;
448 public static boolean delete(File file
){
449 File
[] files
= file
.listFiles();
451 for (File file1
: files
) {
452 if (!delete(file1
)) return false;
456 for (int i
= 0; i
< 10; i
++){
457 if (file
.delete() || !file
.exists()) return true;
461 catch (InterruptedException ignored
) {
468 public static boolean createParentDirs(File file
) {
469 if (!file
.exists()) {
470 String parentDirPath
= file
.getParent();
471 if (parentDirPath
!= null) {
472 final File parentFile
= new File(parentDirPath
);
473 return parentFile
.exists() && parentFile
.isDirectory() || parentFile
.mkdirs();
479 public static boolean createIfDoesntExist(File file
) {
480 if (file
.exists()) return true;
482 if (!createParentDirs(file
)) return false;
484 OutputStream s
= new FileOutputStream(file
);
488 catch (IOException e
) {
493 public static boolean ensureCanCreateFile(File file
) {
494 if (file
.exists()) return file
.canWrite();
495 if (!createIfDoesntExist(file
)) return false;
499 public static void copy(File fromFile
, File toFile
) throws IOException
{
500 performCopy(fromFile
, toFile
, true);
503 public static void copyContent(File fromFile
, File toFile
) throws IOException
{
504 performCopy(fromFile
, toFile
, false);
507 private static void performCopy(File fromFile
, File toFile
, final boolean syncTimestamp
) throws IOException
{
508 FileOutputStream fos
;
510 fos
= new FileOutputStream(toFile
);
512 catch (FileNotFoundException e
) {
513 File parentFile
= toFile
.getParentFile();
514 if (parentFile
== null) {
515 final IOException ioException
= new IOException("parent file is null for " + toFile
.getPath());
516 ioException
.initCause(e
);
519 createParentDirs(toFile
);
520 fos
= new FileOutputStream(toFile
);
523 if (Patches
.FILE_CHANNEL_TRANSFER_BROKEN
) {
524 FileInputStream fis
= new FileInputStream(fromFile
);
534 FileChannel fromChannel
= new FileInputStream(fromFile
).getChannel();
535 FileChannel toChannel
= fos
.getChannel();
537 fromChannel
.transferTo(0, Long
.MAX_VALUE
, toChannel
);
546 toFile
.setLastModified(fromFile
.lastModified());
550 public static void copy(InputStream inputStream
, OutputStream outputStream
) throws IOException
{
551 final byte[] buffer
= BUFFER
.get();
553 int read
= inputStream
.read(buffer
);
555 outputStream
.write(buffer
, 0, read
);
559 public static void copy(InputStream inputStream
, int size
, OutputStream outputStream
) throws IOException
{
560 final byte[] buffer
= BUFFER
.get();
563 int read
= inputStream
.read(buffer
, 0, Math
.min(buffer
.length
, toRead
));
566 outputStream
.write(buffer
, 0, read
);
570 public static void copyDir(File fromDir
, File toDir
) throws IOException
{
571 copyDir(fromDir
, toDir
, true);
574 public static void copyDir(File fromDir
, File toDir
, boolean copySystemFiles
) throws IOException
{
575 copyDir(fromDir
, toDir
, copySystemFiles ?
null : new FileFilter() {
576 public boolean accept(File file
) {
577 return !file
.getName().startsWith(".");
582 public static void copyDir(File fromDir
, File toDir
, final @Nullable FileFilter filter
) throws IOException
{
584 if (isAncestor(fromDir
, toDir
, true)) {
585 LOG
.error(fromDir
.getAbsolutePath() + " is ancestor of " + toDir
+ ". Can't copy to itself.");
588 File
[] files
= fromDir
.listFiles();
589 if(files
== null) throw new IOException(CommonBundle
.message("exception.directory.is.invalid", fromDir
.getPath()));
590 if(!fromDir
.canRead()) throw new IOException(CommonBundle
.message("exception.directory.is.not.readable", fromDir
.getPath()));
591 for (File file
: files
) {
592 if (filter
!= null && !filter
.accept(file
)) {
595 if (file
.isDirectory()) {
596 copyDir(file
, new File(toDir
, file
.getName()), filter
);
599 copy(file
, new File(toDir
, file
.getName()));
604 public static String
getNameWithoutExtension(File file
) {
605 return getNameWithoutExtension(file
.getName());
608 public static String
getNameWithoutExtension(String name
) {
609 int i
= name
.lastIndexOf('.');
611 name
= name
.substring(0, i
);
616 public static String
createSequentFileName(File aParentFolder
, @NonNls String aFilePrefix
, String aExtension
) {
617 return findSequentNonexistentFile(aParentFolder
, aFilePrefix
, aExtension
).getName();
620 public static File
findSequentNonexistentFile(final File aParentFolder
, @NonNls final String aFilePrefix
, final String aExtension
) {
622 String ext
= 0 == aExtension
.length() ?
"" : "." + aExtension
;
624 File candidate
= new File(aParentFolder
, aFilePrefix
+ ext
);
625 while (candidate
.exists()) {
627 candidate
= new File(aParentFolder
, aFilePrefix
+ Integer
.toString(postfix
) + ext
);
632 public static String
toSystemDependentName(@NonNls @NotNull String aFileName
) {
633 return aFileName
.replace('/', File
.separatorChar
).replace('\\', File
.separatorChar
);
636 public static String
toSystemIndependentName(@NonNls @NotNull String aFileName
) {
637 return aFileName
.replace('\\', '/');
640 public static String
unquote(String urlString
) {
641 urlString
= urlString
.replace('/', File
.separatorChar
);
642 return URLUtil
.unescapePercentSequences(urlString
);
645 public static boolean isFilePathAcceptable(File file
, @Nullable FileFilter fileFilter
) {
647 if (fileFilter
!= null && !fileFilter
.accept(file
)) return false;
648 file
= file
.getParentFile();
650 while (file
!= null);
654 public static void rename(final File source
, final File target
) throws IOException
{
655 if (source
.renameTo(target
)) return;
656 if (!source
.exists()) return;
658 copy(source
, target
);
662 public static boolean startsWith(@NonNls String path1
, @NonNls String path2
) {
663 return startsWith(path1
, path2
, SystemInfo
.isFileSystemCaseSensitive
);
666 public static boolean startsWith(final String path1
, final String path2
, final boolean caseSensitive
) {
667 final int length1
= path1
.length();
668 final int length2
= path2
.length();
669 if (length2
== 0) return true;
670 if (length2
> length1
) return false;
671 if (!path1
.regionMatches(!caseSensitive
, 0, path2
, 0, length2
)) return false;
672 if (length1
== length2
) return true;
673 char last2
= path2
.charAt(length2
- 1);
675 if (last2
== '/' || last2
== File
.separatorChar
) {
676 next1
= path1
.charAt(length2
-1);
679 next1
= path1
.charAt(length2
);
681 return next1
== '/' || next1
== File
.separatorChar
;
684 public static boolean pathsEqual(String path1
, String path2
) {
685 return SystemInfo
.isFileSystemCaseSensitive? path1
.equals(path2
) : path1
.equalsIgnoreCase(path2
);
688 public static int comparePaths(String path1
, String path2
) {
689 return SystemInfo
.isFileSystemCaseSensitive? path1
.compareTo(path2
) : path1
.compareToIgnoreCase(path2
);
692 public static int pathHashCode(String path
) {
693 return SystemInfo
.isFileSystemCaseSensitive? path
.hashCode() : path
.toLowerCase().hashCode();
697 public static String
getExtension(@NotNull String fileName
) {
698 int index
= fileName
.lastIndexOf('.');
699 if (index
< 0) return "";
700 return fileName
.substring(index
+ 1).toLowerCase();
704 public static String
resolveShortWindowsName(@NotNull final String path
) throws IOException
{
705 if (SystemInfo
.isWindows
) {
706 //todo: this resolves symlinks on Windows, but we'd rather not do it
707 return new File(path
.replace(File
.separatorChar
, '/')).getCanonicalPath();
712 public static void collectMatchedFiles(final File root
, final Pattern pattern
, final List
<File
> files
) {
713 collectMatchedFiles(root
, root
, pattern
, files
);
716 private static void collectMatchedFiles(final File absoluteRoot
, final File root
, final Pattern pattern
, final List
<File
> files
) {
717 final File
[] dirs
= root
.listFiles();
718 if (dirs
== null) return;
719 for (File dir
: dirs
) {
721 final String path
= toSystemIndependentName(getRelativePath(absoluteRoot
, dir
));
722 if (pattern
.matcher(path
).matches()) {
726 collectMatchedFiles(absoluteRoot
, dir
, pattern
, files
);
732 public static String
convertAntToRegexp(String antPattern
) {
733 return convertAntToRegexp(antPattern
, true);
737 * @param antPattern ant-style path pattern
738 * @return java regexp pattern.
739 * Note that no matter whether forward or backward slashes were used in the antPattern
740 * the returned regexp pattern will use forward slashes ('/') as file separators.
741 * Paths containing windows-style backslashes must be converted before matching against the resulting regexp
742 * @see com.intellij.openapi.util.io.FileUtil#toSystemIndependentName
745 public static String
convertAntToRegexp(String antPattern
, boolean ignoreStartingSlash
) {
746 final StringBuilder builder
= new StringBuilder(antPattern
.length());
747 int asteriskCount
= 0;
748 boolean recursive
= true;
749 final int start
= ignoreStartingSlash
&& (antPattern
.startsWith("/") || antPattern
.startsWith("\\")) ?
1 : 0;
750 for (int idx
= start
; idx
< antPattern
.length(); idx
++) {
751 final char ch
= antPattern
.charAt(idx
);
758 final boolean foundRecursivePattern
= recursive
&& asteriskCount
== 2 && (ch
== '/' || ch
== '\\');
759 final boolean asterisksFound
= asteriskCount
> 0;
762 recursive
= ch
== '/' || ch
== '\\';
764 if (foundRecursivePattern
) {
765 builder
.append("(?:[^/]+/)*?");
770 builder
.append("[^/]*?");
773 if (ch
== '(' || ch
== ')' || ch
== '[' || ch
== ']' || ch
== '^' || ch
== '$' || ch
== '.' || ch
== '{' || ch
== '}' || ch
== '+' || ch
== '|') {
774 // quote regexp-specific symbols
775 builder
.append('\\').append(ch
);
779 builder
.append("[^/]{1}");
789 // handle ant shorthand: mypackage/test/ is interpreted as if it were mypackage/test/**
790 final boolean isTrailingSlash
= builder
.length() > 0 && builder
.charAt(builder
.length() - 1) == '/';
791 if (asteriskCount
== 0 && isTrailingSlash
|| recursive
&& asteriskCount
== 2) {
792 if (isTrailingSlash
) {
793 builder
.setLength(builder
.length() - 1);
795 if (builder
.length() == 0) {
796 builder
.append(".*");
799 builder
.append("(?:$|/.+)");
802 else if (asteriskCount
> 0) {
803 builder
.append("[^/]*?");
805 return builder
.toString();
808 public static boolean moveDirWithContent(File fromDir
, File toDir
) {
809 if (!toDir
.exists()) return fromDir
.renameTo(toDir
);
811 File
[] files
= fromDir
.listFiles();
812 if (files
== null) return false;
814 boolean success
= true;
816 for (File fromFile
: files
) {
817 File toFile
= new File(toDir
, fromFile
.getName());
818 success
= success
&& fromFile
.renameTo(toFile
);
825 public static String
sanitizeFileName(String name
) {
826 StringBuilder result
= new StringBuilder();
828 for (int i
= 0; i
< name
.length(); i
++) {
829 final char ch
= name
.charAt(i
);
831 if (ch
> 0 && ch
< 255) {
832 if (Character
.isLetterOrDigit(ch
)) {
845 return result
.toString();
848 private static final Method IO_FILE_CAN_EXECUTE_METHOD
;
852 method
= File
.class.getDeclaredMethod("canExecute", boolean.class);
854 catch (NoSuchMethodException e
) {
857 IO_FILE_CAN_EXECUTE_METHOD
= method
;
860 public static boolean canCallCanExecute() {
861 return IO_FILE_CAN_EXECUTE_METHOD
!= null;
864 public static boolean canExecute(File file
) {
866 return ((Boolean
)IO_FILE_CAN_EXECUTE_METHOD
.invoke(file
));
868 catch (Exception e
) {
874 //File.setWritable() since java 6.0
875 private static final Method IO_FILE_SET_WRITABLE_METHOD
;
879 method
= File
.class.getDeclaredMethod("setWritable", boolean.class);
881 catch (NoSuchMethodException e
) {
884 IO_FILE_SET_WRITABLE_METHOD
= method
;
886 public static void setReadOnlyAttribute(String path
, boolean readOnlyStatus
) throws IOException
{
887 if (IO_FILE_SET_WRITABLE_METHOD
!= null) {
889 IO_FILE_SET_WRITABLE_METHOD
.invoke(new File(path
), !readOnlyStatus
);
892 catch (IllegalAccessException e
) {
895 catch (InvocationTargetException e
) {
900 if (SystemInfo
.isWindows
) {
901 process
= Runtime
.getRuntime().exec(new String
[]{"attrib", readOnlyStatus ?
"+r" : "-r", path
});
903 else { // UNIXes go here
904 process
= Runtime
.getRuntime().exec(new String
[]{"chmod", readOnlyStatus ?
"u-w" : "u+w", path
});
909 catch (InterruptedException ignored
) {
914 * The method File.setExecutalbe() (which is avaialable since java 6.0)
916 private static final Method IO_FILE_SET_EXECUTABLE_METHOD
;
920 method
= File
.class.getDeclaredMethod("setExecutable", boolean.class);
922 catch (NoSuchMethodException e
) {
925 IO_FILE_SET_EXECUTABLE_METHOD
= method
;
929 * Set executable attibute, it makes sense only on non-windows platforms.
931 * @param path the path to use
932 * @param executableFlag new value of executable attribute
933 * @throws IOException if there is a problem with setting the flag
935 public static void setExectuableAttribute(String path
, boolean executableFlag
) throws IOException
{
936 if (IO_FILE_SET_EXECUTABLE_METHOD
!= null) {
938 IO_FILE_SET_EXECUTABLE_METHOD
.invoke(new File(path
), executableFlag
);
941 catch (IllegalAccessException e
) {
944 catch (InvocationTargetException e
) {
948 if (!SystemInfo
.isWindows
) {
950 Process process
= Runtime
.getRuntime().exec(new String
[]{"chmod", executableFlag ?
"u+x" : "u-x", path
});
954 catch (InterruptedException ignored
) {
959 public static void writeToFile(final File file
, final byte[] text
) throws IOException
{
960 writeToFile(file
, text
, false);
963 public static void writeToFile(final File file
, final byte[] text
, boolean append
) throws IOException
{
964 createParentDirs(file
);
965 OutputStream stream
= new BufferedOutputStream(new FileOutputStream(file
, append
));
975 public static boolean processFilesRecursively(final File root
, final Processor
<File
> processor
) {
976 final LinkedList
<File
> queue
= new LinkedList
<File
>();
978 while (!queue
.isEmpty()) {
979 final File file
= queue
.removeFirst();
980 if (!processor
.process(file
)) return false;
981 if (file
.isDirectory()) {
982 final File
[] children
= file
.listFiles();
983 if (children
!= null) {
984 queue
.addAll(Arrays
.asList(children
));
992 public static File
findFirstThatExist(String
... paths
) {
993 for (String path
: paths
) {
994 if (!StringUtil
.isEmptyOrSpaces(path
)) {
995 File file
= new File(toSystemDependentName(path
));
996 if (file
.exists()) return file
;