ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / platform / util / src / com / intellij / openapi / util / io / FileUtil.java
blob850b4d56fead6e3357060a80ac65d9b0c62886ca
1 /*
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;
32 import java.io.*;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.nio.channels.FileChannel;
36 import java.util.*;
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];
55 @Nullable
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;
78 int len = 0;
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;
86 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;
115 while (true) {
116 if (parent == null) {
117 return false;
119 if (parent.equals(ancestor)) {
120 return true;
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.
134 @Nullable
135 public static File getParentFile(final File file) {
136 int skipCount = 0;
137 File parentFile = file;
138 while (true) {
139 parentFile = parentFile.getParentFile();
140 if (parentFile == null) {
141 return null;
143 if (".".equals(parentFile.getName())) {
144 continue;
146 if ("..".equals(parentFile.getName())) {
147 skipCount++;
148 continue;
150 if (skipCount > 0) {
151 skipCount--;
152 continue;
154 return parentFile;
158 @NotNull
159 public static char[] loadFileText(File file) throws IOException {
160 return loadFileText(file, null);
163 @NotNull
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);
167 try{
168 return loadText(reader, (int)file.length());
170 finally{
171 reader.close();
175 @NotNull
176 public static char[] loadText(Reader reader, int length) throws IOException {
177 char[] chars = new char[length];
178 int count = 0;
179 while (count < chars.length) {
180 int n = reader.read(chars, count, chars.length - count);
181 if (n <= 0) break;
182 count += n;
184 if (count == chars.length){
185 return chars;
187 else{
188 char[] newChars = new char[count];
189 System.arraycopy(chars, 0, newChars, 0, count);
190 return newChars;
194 @NotNull
195 public static byte[] loadFileBytes(File file) throws IOException {
196 byte[] bytes;
197 final InputStream stream = new FileInputStream(file);
198 try{
199 final long len = file.length();
200 if (len < 0) {
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);
210 finally{
211 stream.close();
213 return bytes;
216 @NotNull
217 public static byte[] loadBytes(InputStream stream, int length) throws IOException{
218 byte[] bytes = new byte[length];
219 int count = 0;
220 while(count < length) {
221 int n = stream.read(bytes, count, length - count);
222 if (n <= 0) break;
223 count += n;
225 return bytes;
228 @NotNull
229 public static byte[] loadBytes(InputStream stream) throws IOException{
230 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
231 final byte[] bytes = BUFFER.get();
232 while(true) {
233 int n = stream.read(bytes, 0, bytes.length);
234 if (n <= 0) break;
235 buffer.write(bytes, 0, n);
237 buffer.close();
238 return buffer.toByteArray();
241 @NotNull
242 public static String loadTextAndClose(Reader reader) throws IOException {
243 try {
244 return new String(adaptiveLoadText(reader));
246 finally {
247 reader.close();
251 @NotNull
252 public static char[] adaptiveLoadText(Reader reader) throws IOException {
253 char[] chars = new char[4096];
254 List<char[]> buffers = null;
255 int count = 0;
256 int total = 0;
257 while (true) {
258 int n = reader.read(chars, count, chars.length-count);
259 if (n <= 0) break;
260 count += n;
261 if (total > 1024*1024*10) throw new FileTooBigException("File too big "+reader);
262 total += n;
263 if (count == chars.length) {
264 if (buffers == null) {
265 buffers = new ArrayList<char[]>();
267 buffers.add(chars);
268 int newLength = Math.min(1024*1024, chars.length * 2);
269 chars = new char[newLength];
270 count = 0;
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);
281 return result;
284 @NotNull
285 public static byte[] adaptiveLoadBytes(InputStream stream) throws IOException{
286 byte[] bytes = new byte[4096];
287 List<byte[]> buffers = null;
288 int count = 0;
289 int total = 0;
290 while (true) {
291 int n = stream.read(bytes, count, bytes.length-count);
292 if (n <= 0) break;
293 count += n;
294 if (total > 1024*1024*10) throw new FileTooBigException("File too big "+stream);
295 total += n;
296 if (count == bytes.length) {
297 if (buffers == null) {
298 buffers = new ArrayList<byte[]>();
300 buffers.add(bytes);
301 int newLength = Math.min(1024*1024, bytes.length * 2);
302 bytes = new byte[newLength];
303 count = 0;
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);
314 return result;
317 public static File createTempDirectory(@NonNls String prefix, @NonNls String suffix) throws IOException{
318 File file = doCreateTempFile(prefix, suffix);
319 file.delete();
320 file.mkdir();
321 return file;
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);
326 file.delete();
327 if (create) {
328 file.createNewFile();
330 return file;
333 public static File createTempFile(@NonNls String prefix, @NonNls String suffix) throws IOException{
334 File file = doCreateTempFile(prefix, suffix);
335 file.delete();
336 file.createNewFile();
337 return file;
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;
350 while(true){
351 try{
352 return File.createTempFile(prefix, suffix, dir).getCanonicalFile();
354 catch(IOException e){ // Win32 createFileExclusively access denied
355 if (++exceptionsCount >= 100) {
356 throw e;
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) {
369 return;
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() {
389 public void run() {
390 final Thread currentThread = Thread.currentThread();
391 currentThread.setPriority(Thread.MIN_PRIORITY);
392 ShutDownTracker.getInstance().registerStopperThread(currentThread);
393 try {
394 for (File tempFile : tempFiles) {
395 delete(tempFile);
398 finally {
399 ShutDownTracker.getInstance().unregisterStopperThread(currentThread);
400 currentThread.setPriority(Thread.NORM_PRIORITY);
405 try {
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");
416 t.start();
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);
429 if (isSameDrive) {
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)) {
434 return tempFile;
438 delete(file);
440 return null;
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();
454 if (files != null) {
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;
462 try {
463 Thread.sleep(10);
465 catch (InterruptedException ignored) {
469 return false;
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();
480 return false;
483 public static boolean createIfDoesntExist(File file) {
484 if (file.exists()) return true;
485 try {
486 if (!createParentDirs(file)) return false;
488 OutputStream s = new FileOutputStream(file);
489 s.close();
490 return true;
492 catch (IOException e) {
493 return false;
497 public static boolean ensureCanCreateFile(File file) {
498 if (file.exists()) return file.canWrite();
499 if (!createIfDoesntExist(file)) return false;
500 return delete(file);
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;
513 try {
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);
521 throw ioException;
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);
529 try {
530 copy(fis, fos);
532 finally {
533 fis.close();
534 fos.close();
537 else {
538 FileChannel fromChannel = new FileInputStream(fromFile).getChannel();
539 FileChannel toChannel = fos.getChannel();
540 try {
541 fromChannel.transferTo(0, Long.MAX_VALUE, toChannel);
543 finally {
544 fromChannel.close();
545 toChannel.close();
549 if (syncTimestamp) {
550 toFile.setLastModified(fromFile.lastModified());
554 public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
555 final byte[] buffer = BUFFER.get();
556 while (true) {
557 int read = inputStream.read(buffer);
558 if (read < 0) break;
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();
565 int toRead = size;
566 while (toRead > 0) {
567 int read = inputStream.read(buffer, 0, Math.min(buffer.length, toRead));
568 if (read < 0) break;
569 toRead -= read;
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 {
587 toDir.mkdirs();
588 if (isAncestor(fromDir, toDir, true)) {
589 LOG.error(fromDir.getAbsolutePath() + " is ancestor of " + toDir + ". Can't copy to itself.");
590 return;
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)) {
597 continue;
599 if (file.isDirectory()) {
600 copyDir(file, new File(toDir, file.getName()), filter);
602 else {
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('.');
614 if (i != -1) {
615 name = name.substring(0, i);
617 return name;
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) {
625 int postfix = 0;
626 String ext = 0 == aExtension.length() ? "" : "." + aExtension;
628 File candidate = new File(aParentFolder, aFilePrefix + ext);
629 while (candidate.exists()) {
630 postfix++;
631 candidate = new File(aParentFolder, aFilePrefix + Integer.toString(postfix) + ext);
633 return candidate;
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) {
654 do {
655 if (fileFilter != null && !fileFilter.accept(file)) return false;
656 file = file.getParentFile();
658 while (file != null);
659 return true;
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);
667 delete(source);
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);
682 char next1;
683 if (last2 == '/' || last2 == File.separatorChar) {
684 next1 = path.charAt(length2 -1);
686 else {
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();
704 @NotNull
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();
711 @NotNull
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();
717 return path;
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) {
728 if (dir.isFile()) {
729 final String path = toSystemIndependentName(getRelativePath(absoluteRoot, dir));
730 if (pattern.matcher(path).matches()) {
731 files.add(dir);
733 } else {
734 collectMatchedFiles(absoluteRoot, dir, pattern, files);
739 @RegExp
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
752 @RegExp
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);
761 if (ch == '*') {
762 asteriskCount++;
763 continue;
766 final boolean foundRecursivePattern = recursive && asteriskCount == 2 && (ch == '/' || ch == '\\');
767 final boolean asterisksFound = asteriskCount > 0;
769 asteriskCount = 0;
770 recursive = ch == '/' || ch == '\\';
772 if (foundRecursivePattern) {
773 builder.append("(?:[^/]+/)*?");
774 continue;
777 if (asterisksFound){
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);
784 continue;
786 if (ch == '?') {
787 builder.append("[^/]{1}");
788 continue;
790 if (ch == '\\') {
791 builder.append('/');
792 continue;
794 builder.append(ch);
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(".*");
806 else {
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);
828 fromDir.delete();
830 return success;
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)) {
841 result.append(ch);
843 else {
844 result.append("_");
847 else {
853 return result.toString();
856 private static final Method IO_FILE_CAN_EXECUTE_METHOD;
857 static {
858 Method method;
859 try {
860 method = File.class.getDeclaredMethod("canExecute", boolean.class);
862 catch (NoSuchMethodException e) {
863 method = null;
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) {
873 try {
874 return ((Boolean)IO_FILE_CAN_EXECUTE_METHOD.invoke(file));
876 catch (Exception e) {
877 LOG.error(e);
878 return false;
882 //File.setWritable() since java 6.0
883 private static final Method IO_FILE_SET_WRITABLE_METHOD;
884 static {
885 Method method;
886 try {
887 method = File.class.getDeclaredMethod("setWritable", boolean.class);
889 catch (NoSuchMethodException e) {
890 method = null;
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) {
896 try {
897 IO_FILE_SET_WRITABLE_METHOD.invoke(new File(path), !readOnlyStatus);
898 return;
900 catch (IllegalAccessException e) {
901 LOG.error(e);
903 catch (InvocationTargetException e) {
904 LOG.error(e);
907 Process process;
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});
914 try {
915 process.waitFor();
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;
925 static {
926 Method method;
927 try {
928 method = File.class.getDeclaredMethod("setExecutable", boolean.class);
930 catch (NoSuchMethodException e) {
931 method = null;
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) {
945 try {
946 IO_FILE_SET_EXECUTABLE_METHOD.invoke(new File(path), executableFlag);
947 return;
949 catch (IllegalAccessException e) {
950 LOG.error(e);
952 catch (InvocationTargetException e) {
953 LOG.error(e);
956 if (!SystemInfo.isWindows) {
957 // UNIXes go here
958 Process process = Runtime.getRuntime().exec(new String[]{"chmod", executableFlag ? "u+x" : "u-x", path});
959 try {
960 process.waitFor();
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));
974 try {
975 stream.write(text);
977 finally {
978 stream.close();
983 public static boolean processFilesRecursively(final File root, final Processor<File> processor) {
984 final LinkedList<File> queue = new LinkedList<File>();
985 queue.add(root);
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));
996 return true;
999 @Nullable
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;
1008 return null;