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];
50 // do not use channels to copy files larger than 5 Mb because of possible MapFailed error
51 private static final long CHANNELS_COPYING_LIMIT
= 5L * 1024L * 1024L;
53 //private static final byte[] BUFFER = new byte[1024 * 20];
56 public static String
getRelativePath(File base
, File file
) {
57 if (base
== null || file
== null) return null;
59 if (!base
.isDirectory()) {
60 base
= base
.getParentFile();
61 if (base
== null) return null;
64 if (base
.equals(file
)) return ".";
66 final String filePath
= file
.getAbsolutePath();
67 String basePath
= base
.getAbsolutePath();
68 return getRelativePath(basePath
, filePath
, File
.separatorChar
);
71 public static String
getRelativePath(String basePath
, String filePath
, final char separator
) {
72 return getRelativePath(basePath
, filePath
, separator
, SystemInfo
.isFileSystemCaseSensitive
);
75 public static String
getRelativePath(String basePath
, String filePath
, final char separator
, final boolean caseSensitive
) {
76 if (!StringUtil
.endsWithChar(basePath
, separator
)) basePath
+= separator
;
79 int lastSeparatorIndex
= 0; // need this for cases like this: base="/temp/abcde/base" and file="/temp/ab"
80 String basePathToCompare
= caseSensitive ? basePath
: basePath
.toLowerCase();
81 String filePathToCompare
= caseSensitive ? filePath
: filePath
.toLowerCase();
82 while (len
< filePath
.length() && len
< basePath
.length() && filePathToCompare
.charAt(len
) == basePathToCompare
.charAt(len
)) {
83 if (basePath
.charAt(len
) == separator
) {
84 lastSeparatorIndex
= len
;
89 if (len
== 0) return null;
91 StringBuilder relativePath
= new StringBuilder();
92 for (int i
=len
; i
< basePath
.length(); i
++) {
93 if (basePath
.charAt(i
) == separator
) {
94 relativePath
.append("..");
95 relativePath
.append(separator
);
98 relativePath
.append(filePath
.substring(lastSeparatorIndex
+ 1));
100 return relativePath
.toString();
104 * Check if the {@code ancestor} is an ancestor of {@code file}.
106 * @param ancestor the file
107 * @param file the file
108 * @param strict if {@code false} then this method returns {@code true} if {@code ancestor}
109 * and {@code file} are equal
110 * @return {@code true} if {@code ancestor} is parent of {@code file}; {@code false} otherwise
111 * @throws IOException this exception is never thrown and left here for backward compatibilty
113 public static boolean isAncestor(File ancestor
, File file
, boolean strict
) throws IOException
{
114 File parent
= strict ?
getParentFile(file
) : file
;
116 if (parent
== null) {
119 if (parent
.equals(ancestor
)) {
122 parent
= getParentFile(parent
);
127 * Get parent for the file. The method correctly
128 * processes "." and ".." in file names. The name
129 * remains relative if was relative before.
131 * @param file a file to analyze
132 * @return a parent or the null if the file has no parent.
135 public static File
getParentFile(final File file
) {
137 File parentFile
= file
;
139 parentFile
= parentFile
.getParentFile();
140 if (parentFile
== null) {
143 if (".".equals(parentFile
.getName())) {
146 if ("..".equals(parentFile
.getName())) {
159 public static char[] loadFileText(File file
) throws IOException
{
160 return loadFileText(file
, null);
164 public static char[] loadFileText(File file
, @NonNls String encoding
) throws IOException
{
165 InputStream stream
= new FileInputStream(file
);
166 Reader reader
= encoding
== null ?
new InputStreamReader(stream
) : new InputStreamReader(stream
, encoding
);
168 return loadText(reader
, (int)file
.length());
176 public static char[] loadText(Reader reader
, int length
) throws IOException
{
177 char[] chars
= new char[length
];
179 while (count
< chars
.length
) {
180 int n
= reader
.read(chars
, count
, chars
.length
- count
);
184 if (count
== chars
.length
){
188 char[] newChars
= new char[count
];
189 System
.arraycopy(chars
, 0, newChars
, 0, count
);
195 public static byte[] loadFileBytes(File file
) throws IOException
{
197 final InputStream stream
= new FileInputStream(file
);
199 final long len
= file
.length();
201 throw new IOException("File length reported negative, probably doesn't exist");
204 if (len
> 100 * MEGABYTE
) {
205 throw new FileTooBigException("Attempt to load '" + file
+ "' in memory buffer, file length is " + len
+ " bytes.");
208 bytes
= loadBytes(stream
, (int)len
);
217 public static byte[] loadBytes(InputStream stream
, int length
) throws IOException
{
218 byte[] bytes
= new byte[length
];
220 while(count
< length
) {
221 int n
= stream
.read(bytes
, count
, length
- count
);
229 public static byte[] loadBytes(InputStream stream
) throws IOException
{
230 ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
231 final byte[] bytes
= BUFFER
.get();
233 int n
= stream
.read(bytes
, 0, bytes
.length
);
235 buffer
.write(bytes
, 0, n
);
238 return buffer
.toByteArray();
242 public static String
loadTextAndClose(Reader reader
) throws IOException
{
244 return new String(adaptiveLoadText(reader
));
252 public static char[] adaptiveLoadText(Reader reader
) throws IOException
{
253 char[] chars
= new char[4096];
254 List
<char[]> buffers
= null;
258 int n
= reader
.read(chars
, count
, chars
.length
-count
);
261 if (total
> 1024*1024*10) throw new FileTooBigException("File too big "+reader
);
263 if (count
== chars
.length
) {
264 if (buffers
== null) {
265 buffers
= new ArrayList
<char[]>();
268 int newLength
= Math
.min(1024*1024, chars
.length
* 2);
269 chars
= new char[newLength
];
273 char[] result
= new char[total
];
274 if (buffers
!= null) {
275 for (char[] buffer
: buffers
) {
276 System
.arraycopy(buffer
, 0, result
, result
.length
- total
, buffer
.length
);
277 total
-= buffer
.length
;
280 System
.arraycopy(chars
, 0, result
, result
.length
- total
, total
);
285 public static byte[] adaptiveLoadBytes(InputStream stream
) throws IOException
{
286 byte[] bytes
= new byte[4096];
287 List
<byte[]> buffers
= null;
291 int n
= stream
.read(bytes
, count
, bytes
.length
-count
);
294 if (total
> 1024*1024*10) throw new FileTooBigException("File too big "+stream
);
296 if (count
== bytes
.length
) {
297 if (buffers
== null) {
298 buffers
= new ArrayList
<byte[]>();
301 int newLength
= Math
.min(1024*1024, bytes
.length
* 2);
302 bytes
= new byte[newLength
];
306 byte[] result
= new byte[total
];
307 if (buffers
!= null) {
308 for (byte[] buffer
: buffers
) {
309 System
.arraycopy(buffer
, 0, result
, result
.length
- total
, buffer
.length
);
310 total
-= buffer
.length
;
313 System
.arraycopy(bytes
, 0, result
, result
.length
- total
, total
);
317 public static File
createTempDirectory(@NonNls String prefix
, @NonNls String suffix
) throws IOException
{
318 File file
= doCreateTempFile(prefix
, suffix
);
324 public static File
createTempFile(@NonNls final File dir
, @NonNls String prefix
, @NonNls String suffix
, final boolean create
) throws IOException
{
325 File file
= doCreateTempFile(prefix
, suffix
, dir
);
328 file
.createNewFile();
333 public static File
createTempFile(@NonNls String prefix
, @NonNls String suffix
) throws IOException
{
334 File file
= doCreateTempFile(prefix
, suffix
);
336 file
.createNewFile();
340 private static File
doCreateTempFile(String prefix
, String suffix
) throws IOException
{
341 return doCreateTempFile(prefix
, suffix
, new File(getTempDirectory()));
344 private static File
doCreateTempFile(String prefix
, String suffix
, final File dir
) throws IOException
{
345 if (prefix
.length() < 3) {
346 prefix
= (prefix
+ "___").substring(0, 3);
349 int exceptionsCount
= 0;
352 return File
.createTempFile(prefix
, suffix
, dir
).getCanonicalFile();
354 catch(IOException e
){ // Win32 createFileExclusively access denied
355 if (++exceptionsCount
>= 100) {
362 public static String
getTempDirectory() {
363 return System
.getProperty("java.io.tmpdir");
366 public static void asyncDelete(@NotNull File file
) {
367 final File tempFile
= renameToTempFileOrDelete(file
);
368 if (tempFile
== null) {
371 startDeletionThread(tempFile
);
374 public static void asyncDelete(@NotNull Collection
<File
> files
) {
375 List
<File
> tempFiles
= new ArrayList
<File
>();
376 for (File file
: files
) {
377 final File tempFile
= renameToTempFileOrDelete(file
);
378 if (tempFile
!= null) {
379 tempFiles
.add(tempFile
);
382 if (!tempFiles
.isEmpty()) {
383 startDeletionThread(tempFiles
.toArray(new File
[tempFiles
.size()]));
387 private static void startDeletionThread(@NotNull final File
... tempFiles
) {
388 final Runnable deleteFilesTask
= new Runnable() {
390 final Thread currentThread
= Thread
.currentThread();
391 currentThread
.setPriority(Thread
.MIN_PRIORITY
);
392 ShutDownTracker
.getInstance().registerStopperThread(currentThread
);
394 for (File tempFile
: tempFiles
) {
399 ShutDownTracker
.getInstance().unregisterStopperThread(currentThread
);
400 currentThread
.setPriority(Thread
.NORM_PRIORITY
);
406 // Attempt to execute on pooled thread
407 final Class
<?
> aClass
= Class
.forName("com.intellij.openapi.application.ApplicationManager");
408 final Method getApplicationMethod
= aClass
.getMethod("getApplication");
409 final Object application
= getApplicationMethod
.invoke(null);
410 final Method executeOnPooledThreadMethod
= application
.getClass().getMethod("executeOnPooledThread", Runnable
.class);
411 executeOnPooledThreadMethod
.invoke(application
, deleteFilesTask
);
413 catch (Exception e
) {
414 //noinspection HardCodedStringLiteral
415 Thread t
= new Thread(deleteFilesTask
, "File deletion thread");
420 private static File
renameToTempFileOrDelete(File file
) {
421 final File tempDir
= new File(getTempDirectory());
422 boolean isSameDrive
= true;
423 if (SystemInfo
.isWindows
) {
424 String tempDirDrive
= tempDir
.getAbsolutePath().substring(0, 2);
425 String fileDrive
= file
.getAbsolutePath().substring(0, 2);
426 isSameDrive
= tempDirDrive
.equalsIgnoreCase(fileDrive
);
430 // the optimization is reasonable only if destination dir is located on the same drive
431 final String originalFileName
= file
.getName();
432 File tempFile
= getTempFile(originalFileName
, tempDir
);
433 if (file
.renameTo(tempFile
)) {
443 private static File
getTempFile(String originalFileName
, File parent
) {
444 int randomSuffix
= (int)(System
.currentTimeMillis() % 1000);
445 for (int i
= randomSuffix
; ; i
++) {
446 @NonNls String name
= "___" + originalFileName
+ i
+ ".__del__";
447 File tempFile
= new File(parent
, name
);
448 if (!tempFile
.exists()) return tempFile
;
452 public static boolean delete(File file
){
453 File
[] files
= file
.listFiles();
455 for (File file1
: files
) {
456 if (!delete(file1
)) return false;
460 for (int i
= 0; i
< 10; i
++){
461 if (file
.delete() || !file
.exists()) return true;
465 catch (InterruptedException ignored
) {
472 public static boolean createParentDirs(File file
) {
473 if (!file
.exists()) {
474 String parentDirPath
= file
.getParent();
475 if (parentDirPath
!= null) {
476 final File parentFile
= new File(parentDirPath
);
477 return parentFile
.exists() && parentFile
.isDirectory() || parentFile
.mkdirs();
483 public static boolean createIfDoesntExist(File file
) {
484 if (file
.exists()) return true;
486 if (!createParentDirs(file
)) return false;
488 OutputStream s
= new FileOutputStream(file
);
492 catch (IOException e
) {
497 public static boolean ensureCanCreateFile(File file
) {
498 if (file
.exists()) return file
.canWrite();
499 if (!createIfDoesntExist(file
)) return false;
503 public static void copy(File fromFile
, File toFile
) throws IOException
{
504 performCopy(fromFile
, toFile
, true);
507 public static void copyContent(File fromFile
, File toFile
) throws IOException
{
508 performCopy(fromFile
, toFile
, false);
511 private static void performCopy(File fromFile
, File toFile
, final boolean syncTimestamp
) throws IOException
{
512 FileOutputStream fos
;
514 fos
= new FileOutputStream(toFile
);
516 catch (FileNotFoundException e
) {
517 File parentFile
= toFile
.getParentFile();
518 if (parentFile
== null) {
519 final IOException ioException
= new IOException("parent file is null for " + toFile
.getPath());
520 ioException
.initCause(e
);
523 createParentDirs(toFile
);
524 fos
= new FileOutputStream(toFile
);
527 if (Patches
.FILE_CHANNEL_TRANSFER_BROKEN
|| fromFile
.length() > CHANNELS_COPYING_LIMIT
) {
528 FileInputStream fis
= new FileInputStream(fromFile
);
538 FileChannel fromChannel
= new FileInputStream(fromFile
).getChannel();
539 FileChannel toChannel
= fos
.getChannel();
541 fromChannel
.transferTo(0, Long
.MAX_VALUE
, toChannel
);
550 toFile
.setLastModified(fromFile
.lastModified());
554 public static void copy(InputStream inputStream
, OutputStream outputStream
) throws IOException
{
555 final byte[] buffer
= BUFFER
.get();
557 int read
= inputStream
.read(buffer
);
559 outputStream
.write(buffer
, 0, read
);
563 public static void copy(InputStream inputStream
, int size
, OutputStream outputStream
) throws IOException
{
564 final byte[] buffer
= BUFFER
.get();
567 int read
= inputStream
.read(buffer
, 0, Math
.min(buffer
.length
, toRead
));
570 outputStream
.write(buffer
, 0, read
);
574 public static void copyDir(File fromDir
, File toDir
) throws IOException
{
575 copyDir(fromDir
, toDir
, true);
578 public static void copyDir(File fromDir
, File toDir
, boolean copySystemFiles
) throws IOException
{
579 copyDir(fromDir
, toDir
, copySystemFiles ?
null : new FileFilter() {
580 public boolean accept(File file
) {
581 return !file
.getName().startsWith(".");
586 public static void copyDir(File fromDir
, File toDir
, final @Nullable FileFilter filter
) throws IOException
{
588 if (isAncestor(fromDir
, toDir
, true)) {
589 LOG
.error(fromDir
.getAbsolutePath() + " is ancestor of " + toDir
+ ". Can't copy to itself.");
592 File
[] files
= fromDir
.listFiles();
593 if(files
== null) throw new IOException(CommonBundle
.message("exception.directory.is.invalid", fromDir
.getPath()));
594 if(!fromDir
.canRead()) throw new IOException(CommonBundle
.message("exception.directory.is.not.readable", fromDir
.getPath()));
595 for (File file
: files
) {
596 if (filter
!= null && !filter
.accept(file
)) {
599 if (file
.isDirectory()) {
600 copyDir(file
, new File(toDir
, file
.getName()), filter
);
603 copy(file
, new File(toDir
, file
.getName()));
608 public static String
getNameWithoutExtension(File file
) {
609 return getNameWithoutExtension(file
.getName());
612 public static String
getNameWithoutExtension(String name
) {
613 int i
= name
.lastIndexOf('.');
615 name
= name
.substring(0, i
);
620 public static String
createSequentFileName(File aParentFolder
, @NonNls String aFilePrefix
, String aExtension
) {
621 return findSequentNonexistentFile(aParentFolder
, aFilePrefix
, aExtension
).getName();
624 public static File
findSequentNonexistentFile(final File aParentFolder
, @NonNls final String aFilePrefix
, final String aExtension
) {
626 String ext
= 0 == aExtension
.length() ?
"" : "." + aExtension
;
628 File candidate
= new File(aParentFolder
, aFilePrefix
+ ext
);
629 while (candidate
.exists()) {
631 candidate
= new File(aParentFolder
, aFilePrefix
+ Integer
.toString(postfix
) + ext
);
636 public static String
toSystemDependentName(@NonNls @NotNull String aFileName
) {
637 return aFileName
.replace('/', File
.separatorChar
).replace('\\', File
.separatorChar
);
640 public static String
toSystemIndependentName(@NonNls @NotNull String aFileName
) {
641 return aFileName
.replace('\\', '/');
644 public static String
nameToCompare(@NonNls @NotNull String name
) {
645 return (SystemInfo
.isFileSystemCaseSensitive ? name
: name
.toLowerCase()).replace('\\', '/');
648 public static String
unquote(String urlString
) {
649 urlString
= urlString
.replace('/', File
.separatorChar
);
650 return URLUtil
.unescapePercentSequences(urlString
);
653 public static boolean isFilePathAcceptable(File file
, @Nullable FileFilter fileFilter
) {
655 if (fileFilter
!= null && !fileFilter
.accept(file
)) return false;
656 file
= file
.getParentFile();
658 while (file
!= null);
662 public static void rename(final File source
, final File target
) throws IOException
{
663 if (source
.renameTo(target
)) return;
664 if (!source
.exists()) return;
666 copy(source
, target
);
670 public static boolean startsWith(@NonNls String path
, @NonNls String start
) {
671 return startsWith(path
, start
, SystemInfo
.isFileSystemCaseSensitive
);
674 public static boolean startsWith(final String path
, final String start
, final boolean caseSensitive
) {
675 final int length1
= path
.length();
676 final int length2
= start
.length();
677 if (length2
== 0) return true;
678 if (length2
> length1
) return false;
679 if (!path
.regionMatches(!caseSensitive
, 0, start
, 0, length2
)) return false;
680 if (length1
== length2
) return true;
681 char last2
= start
.charAt(length2
- 1);
683 if (last2
== '/' || last2
== File
.separatorChar
) {
684 next1
= path
.charAt(length2
-1);
687 next1
= path
.charAt(length2
);
689 return next1
== '/' || next1
== File
.separatorChar
;
692 public static boolean pathsEqual(String path1
, String path2
) {
693 return SystemInfo
.isFileSystemCaseSensitive? path1
.equals(path2
) : path1
.equalsIgnoreCase(path2
);
696 public static int comparePaths(String path1
, String path2
) {
697 return SystemInfo
.isFileSystemCaseSensitive? path1
.compareTo(path2
) : path1
.compareToIgnoreCase(path2
);
700 public static int pathHashCode(String path
) {
701 return SystemInfo
.isFileSystemCaseSensitive? path
.hashCode() : path
.toLowerCase().hashCode();
705 public static String
getExtension(@NotNull String fileName
) {
706 int index
= fileName
.lastIndexOf('.');
707 if (index
< 0) return "";
708 return fileName
.substring(index
+ 1).toLowerCase();
712 public static String
resolveShortWindowsName(@NotNull final String path
) throws IOException
{
713 if (SystemInfo
.isWindows
) {
714 //todo: this resolves symlinks on Windows, but we'd rather not do it
715 return new File(path
.replace(File
.separatorChar
, '/')).getCanonicalPath();
720 public static void collectMatchedFiles(final File root
, final Pattern pattern
, final List
<File
> files
) {
721 collectMatchedFiles(root
, root
, pattern
, files
);
724 private static void collectMatchedFiles(final File absoluteRoot
, final File root
, final Pattern pattern
, final List
<File
> files
) {
725 final File
[] dirs
= root
.listFiles();
726 if (dirs
== null) return;
727 for (File dir
: dirs
) {
729 final String path
= toSystemIndependentName(getRelativePath(absoluteRoot
, dir
));
730 if (pattern
.matcher(path
).matches()) {
734 collectMatchedFiles(absoluteRoot
, dir
, pattern
, files
);
740 public static String
convertAntToRegexp(String antPattern
) {
741 return convertAntToRegexp(antPattern
, true);
745 * @param antPattern ant-style path pattern
746 * @return java regexp pattern.
747 * Note that no matter whether forward or backward slashes were used in the antPattern
748 * the returned regexp pattern will use forward slashes ('/') as file separators.
749 * Paths containing windows-style backslashes must be converted before matching against the resulting regexp
750 * @see com.intellij.openapi.util.io.FileUtil#toSystemIndependentName
753 public static String
convertAntToRegexp(String antPattern
, boolean ignoreStartingSlash
) {
754 final StringBuilder builder
= new StringBuilder(antPattern
.length());
755 int asteriskCount
= 0;
756 boolean recursive
= true;
757 final int start
= ignoreStartingSlash
&& (antPattern
.startsWith("/") || antPattern
.startsWith("\\")) ?
1 : 0;
758 for (int idx
= start
; idx
< antPattern
.length(); idx
++) {
759 final char ch
= antPattern
.charAt(idx
);
766 final boolean foundRecursivePattern
= recursive
&& asteriskCount
== 2 && (ch
== '/' || ch
== '\\');
767 final boolean asterisksFound
= asteriskCount
> 0;
770 recursive
= ch
== '/' || ch
== '\\';
772 if (foundRecursivePattern
) {
773 builder
.append("(?:[^/]+/)*?");
778 builder
.append("[^/]*?");
781 if (ch
== '(' || ch
== ')' || ch
== '[' || ch
== ']' || ch
== '^' || ch
== '$' || ch
== '.' || ch
== '{' || ch
== '}' || ch
== '+' || ch
== '|') {
782 // quote regexp-specific symbols
783 builder
.append('\\').append(ch
);
787 builder
.append("[^/]{1}");
797 // handle ant shorthand: mypackage/test/ is interpreted as if it were mypackage/test/**
798 final boolean isTrailingSlash
= builder
.length() > 0 && builder
.charAt(builder
.length() - 1) == '/';
799 if (asteriskCount
== 0 && isTrailingSlash
|| recursive
&& asteriskCount
== 2) {
800 if (isTrailingSlash
) {
801 builder
.setLength(builder
.length() - 1);
803 if (builder
.length() == 0) {
804 builder
.append(".*");
807 builder
.append("(?:$|/.+)");
810 else if (asteriskCount
> 0) {
811 builder
.append("[^/]*?");
813 return builder
.toString();
816 public static boolean moveDirWithContent(File fromDir
, File toDir
) {
817 if (!toDir
.exists()) return fromDir
.renameTo(toDir
);
819 File
[] files
= fromDir
.listFiles();
820 if (files
== null) return false;
822 boolean success
= true;
824 for (File fromFile
: files
) {
825 File toFile
= new File(toDir
, fromFile
.getName());
826 success
= success
&& fromFile
.renameTo(toFile
);
833 public static String
sanitizeFileName(String name
) {
834 StringBuilder result
= new StringBuilder();
836 for (int i
= 0; i
< name
.length(); i
++) {
837 final char ch
= name
.charAt(i
);
839 if (ch
> 0 && ch
< 255) {
840 if (Character
.isLetterOrDigit(ch
)) {
853 return result
.toString();
856 private static final Method IO_FILE_CAN_EXECUTE_METHOD
;
860 method
= File
.class.getDeclaredMethod("canExecute", boolean.class);
862 catch (NoSuchMethodException e
) {
865 IO_FILE_CAN_EXECUTE_METHOD
= method
;
868 public static boolean canCallCanExecute() {
869 return IO_FILE_CAN_EXECUTE_METHOD
!= null;
872 public static boolean canExecute(File file
) {
874 return ((Boolean
)IO_FILE_CAN_EXECUTE_METHOD
.invoke(file
));
876 catch (Exception e
) {
882 //File.setWritable() since java 6.0
883 private static final Method IO_FILE_SET_WRITABLE_METHOD
;
887 method
= File
.class.getDeclaredMethod("setWritable", boolean.class);
889 catch (NoSuchMethodException e
) {
892 IO_FILE_SET_WRITABLE_METHOD
= method
;
894 public static void setReadOnlyAttribute(String path
, boolean readOnlyStatus
) throws IOException
{
895 if (IO_FILE_SET_WRITABLE_METHOD
!= null) {
897 IO_FILE_SET_WRITABLE_METHOD
.invoke(new File(path
), !readOnlyStatus
);
900 catch (IllegalAccessException e
) {
903 catch (InvocationTargetException e
) {
908 if (SystemInfo
.isWindows
) {
909 process
= Runtime
.getRuntime().exec(new String
[]{"attrib", readOnlyStatus ?
"+r" : "-r", path
});
911 else { // UNIXes go here
912 process
= Runtime
.getRuntime().exec(new String
[]{"chmod", readOnlyStatus ?
"u-w" : "u+w", path
});
917 catch (InterruptedException ignored
) {
922 * The method File.setExecutalbe() (which is avaialable since java 6.0)
924 private static final Method IO_FILE_SET_EXECUTABLE_METHOD
;
928 method
= File
.class.getDeclaredMethod("setExecutable", boolean.class);
930 catch (NoSuchMethodException e
) {
933 IO_FILE_SET_EXECUTABLE_METHOD
= method
;
937 * Set executable attibute, it makes sense only on non-windows platforms.
939 * @param path the path to use
940 * @param executableFlag new value of executable attribute
941 * @throws IOException if there is a problem with setting the flag
943 public static void setExectuableAttribute(String path
, boolean executableFlag
) throws IOException
{
944 if (IO_FILE_SET_EXECUTABLE_METHOD
!= null) {
946 IO_FILE_SET_EXECUTABLE_METHOD
.invoke(new File(path
), executableFlag
);
949 catch (IllegalAccessException e
) {
952 catch (InvocationTargetException e
) {
956 if (!SystemInfo
.isWindows
) {
958 Process process
= Runtime
.getRuntime().exec(new String
[]{"chmod", executableFlag ?
"u+x" : "u-x", path
});
962 catch (InterruptedException ignored
) {
967 public static void writeToFile(final File file
, final byte[] text
) throws IOException
{
968 writeToFile(file
, text
, false);
971 public static void writeToFile(final File file
, final byte[] text
, boolean append
) throws IOException
{
972 createParentDirs(file
);
973 OutputStream stream
= new BufferedOutputStream(new FileOutputStream(file
, append
));
983 public static boolean processFilesRecursively(final File root
, final Processor
<File
> processor
) {
984 final LinkedList
<File
> queue
= new LinkedList
<File
>();
986 while (!queue
.isEmpty()) {
987 final File file
= queue
.removeFirst();
988 if (!processor
.process(file
)) return false;
989 if (file
.isDirectory()) {
990 final File
[] children
= file
.listFiles();
991 if (children
!= null) {
992 queue
.addAll(Arrays
.asList(children
));
1000 public static File
findFirstThatExist(String
... paths
) {
1001 for (String path
: paths
) {
1002 if (!StringUtil
.isEmptyOrSpaces(path
)) {
1003 File file
= new File(toSystemDependentName(path
));
1004 if (file
.exists()) return file
;