migrated to artifacts
[fedora-idea.git] / platform / util / src / com / intellij / openapi / util / io / FileUtil.java
blob2e0c04e5791204a3d30dba633c04139408ef6bcb
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 org.intellij.lang.annotations.RegExp;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
31 import java.io.*;
32 import java.lang.reflect.InvocationTargetException;
33 import java.lang.reflect.Method;
34 import java.nio.channels.FileChannel;
35 import java.util.*;
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];
50 @Nullable
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;
73 int len = 0;
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;
81 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();
98 /**
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;
110 while (true) {
111 if (parent == null) {
112 return false;
114 if (parent.equals(ancestor)) {
115 return true;
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.
129 @Nullable
130 public static File getParentFile(final File file) {
131 int skipCount = 0;
132 File parentFile = file;
133 while (true) {
134 parentFile = parentFile.getParentFile();
135 if (parentFile == null) {
136 return null;
138 if (".".equals(parentFile.getName())) {
139 continue;
141 if ("..".equals(parentFile.getName())) {
142 skipCount++;
143 continue;
145 if (skipCount > 0) {
146 skipCount--;
147 continue;
149 return parentFile;
153 @NotNull
154 public static char[] loadFileText(File file) throws IOException {
155 return loadFileText(file, null);
158 @NotNull
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);
162 try{
163 return loadText(reader, (int)file.length());
165 finally{
166 reader.close();
170 @NotNull
171 public static char[] loadText(Reader reader, int length) throws IOException {
172 char[] chars = new char[length];
173 int count = 0;
174 while (count < chars.length) {
175 int n = reader.read(chars, count, chars.length - count);
176 if (n <= 0) break;
177 count += n;
179 if (count == chars.length){
180 return chars;
182 else{
183 char[] newChars = new char[count];
184 System.arraycopy(chars, 0, newChars, 0, count);
185 return newChars;
189 @NotNull
190 public static byte[] loadFileBytes(File file) throws IOException {
191 byte[] bytes;
192 final InputStream stream = new FileInputStream(file);
193 try{
194 final long len = file.length();
195 if (len < 0) {
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);
205 finally{
206 stream.close();
208 return bytes;
211 @NotNull
212 public static byte[] loadBytes(InputStream stream, int length) throws IOException{
213 byte[] bytes = new byte[length];
214 int count = 0;
215 while(count < length) {
216 int n = stream.read(bytes, count, length - count);
217 if (n <= 0) break;
218 count += n;
220 return bytes;
223 @NotNull
224 public static byte[] loadBytes(InputStream stream) throws IOException{
225 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
226 final byte[] bytes = BUFFER.get();
227 while(true) {
228 int n = stream.read(bytes, 0, bytes.length);
229 if (n <= 0) break;
230 buffer.write(bytes, 0, n);
232 buffer.close();
233 return buffer.toByteArray();
236 @NotNull
237 public static String loadTextAndClose(Reader reader) throws IOException {
238 try {
239 return new String(adaptiveLoadText(reader));
241 finally {
242 reader.close();
246 @NotNull
247 public static char[] adaptiveLoadText(Reader reader) throws IOException {
248 char[] chars = new char[4096];
249 List<char[]> buffers = null;
250 int count = 0;
251 int total = 0;
252 while (true) {
253 int n = reader.read(chars, count, chars.length-count);
254 if (n <= 0) break;
255 count += n;
256 if (total > 1024*1024*10) throw new FileTooBigException("File too big "+reader);
257 total += n;
258 if (count == chars.length) {
259 if (buffers == null) {
260 buffers = new ArrayList<char[]>();
262 buffers.add(chars);
263 int newLength = Math.min(1024*1024, chars.length * 2);
264 chars = new char[newLength];
265 count = 0;
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);
276 return result;
279 @NotNull
280 public static byte[] adaptiveLoadBytes(InputStream stream) throws IOException{
281 byte[] bytes = new byte[4096];
282 List<byte[]> buffers = null;
283 int count = 0;
284 int total = 0;
285 while (true) {
286 int n = stream.read(bytes, count, bytes.length-count);
287 if (n <= 0) break;
288 count += n;
289 if (total > 1024*1024*10) throw new FileTooBigException("File too big "+stream);
290 total += n;
291 if (count == bytes.length) {
292 if (buffers == null) {
293 buffers = new ArrayList<byte[]>();
295 buffers.add(bytes);
296 int newLength = Math.min(1024*1024, bytes.length * 2);
297 bytes = new byte[newLength];
298 count = 0;
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);
309 return result;
312 public static File createTempDirectory(@NonNls String prefix, @NonNls String suffix) throws IOException{
313 File file = doCreateTempFile(prefix, suffix);
314 file.delete();
315 file.mkdir();
316 return file;
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);
321 file.delete();
322 if (create) {
323 file.createNewFile();
325 return file;
328 public static File createTempFile(@NonNls String prefix, @NonNls String suffix) throws IOException{
329 File file = doCreateTempFile(prefix, suffix);
330 file.delete();
331 file.createNewFile();
332 return file;
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;
345 while(true){
346 try{
347 return File.createTempFile(prefix, suffix, dir).getCanonicalFile();
349 catch(IOException e){ // Win32 createFileExclusively access denied
350 if (++exceptionsCount >= 100) {
351 throw e;
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) {
364 return;
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() {
384 public void run() {
385 final Thread currentThread = Thread.currentThread();
386 currentThread.setPriority(Thread.MIN_PRIORITY);
387 ShutDownTracker.getInstance().registerStopperThread(currentThread);
388 try {
389 for (File tempFile : tempFiles) {
390 delete(tempFile);
393 finally {
394 ShutDownTracker.getInstance().unregisterStopperThread(currentThread);
395 currentThread.setPriority(Thread.NORM_PRIORITY);
400 try {
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");
411 t.start();
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);
424 if (isSameDrive) {
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)) {
429 return tempFile;
433 delete(file);
435 return null;
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();
449 if (files != null) {
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;
457 try {
458 Thread.sleep(10);
460 catch (InterruptedException ignored) {
464 return false;
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();
475 return false;
478 public static boolean createIfDoesntExist(File file) {
479 if (file.exists()) return true;
480 try {
481 if (!createParentDirs(file)) return false;
483 OutputStream s = new FileOutputStream(file);
484 s.close();
485 return true;
487 catch (IOException e) {
488 return false;
492 public static boolean ensureCanCreateFile(File file) {
493 if (file.exists()) return file.canWrite();
494 if (!createIfDoesntExist(file)) return false;
495 return delete(file);
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;
508 try {
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);
516 throw ioException;
518 createParentDirs(toFile);
519 fos = new FileOutputStream(toFile);
522 if (Patches.FILE_CHANNEL_TRANSFER_BROKEN) {
523 FileInputStream fis = new FileInputStream(fromFile);
524 try {
525 copy(fis, fos);
527 finally {
528 fis.close();
529 fos.close();
532 else {
533 FileChannel fromChannel = new FileInputStream(fromFile).getChannel();
534 FileChannel toChannel = fos.getChannel();
535 try {
536 fromChannel.transferTo(0, Long.MAX_VALUE, toChannel);
538 finally {
539 fromChannel.close();
540 toChannel.close();
544 if (syncTimestamp) {
545 toFile.setLastModified(fromFile.lastModified());
549 public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
550 final byte[] buffer = BUFFER.get();
551 while (true) {
552 int read = inputStream.read(buffer);
553 if (read < 0) break;
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();
560 int toRead = size;
561 while (toRead > 0) {
562 int read = inputStream.read(buffer, 0, Math.min(buffer.length, toRead));
563 if (read < 0) break;
564 toRead -= read;
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 {
574 toDir.mkdirs();
575 if (isAncestor(fromDir, toDir, true)) {
576 LOG.error(fromDir.getAbsolutePath() + " is ancestor of " + toDir + ". Can't copy to itself.");
577 return;
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(".")) {
584 continue;
586 if (file.isDirectory()) {
587 copyDir(file, new File(toDir, file.getName()), copySystemFiles);
589 else {
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('.');
601 if (i != -1) {
602 name = name.substring(0, i);
604 return name;
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) {
612 int postfix = 0;
613 String ext = 0 == aExtension.length() ? "" : "." + aExtension;
615 File candidate = new File(aParentFolder, aFilePrefix + ext);
616 while (candidate.exists()) {
617 postfix++;
618 candidate = new File(aParentFolder, aFilePrefix + Integer.toString(postfix) + ext);
620 return candidate;
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) {
639 do {
640 if (fileFilter != null && !fileFilter.accept(file)) return false;
641 file = file.getParentFile();
643 while (file != null);
644 return true;
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);
652 delete(source);
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);
667 char next1;
668 if (last2 == '/' || last2 == File.separatorChar) {
669 next1 = path1.charAt(length2 -1);
671 else {
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();
689 @NotNull
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();
696 @NotNull
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();
702 return path;
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) {
713 if (dir.isFile()) {
714 final String path = toSystemIndependentName(getRelativePath(absoluteRoot, dir));
715 if (pattern.matcher(path).matches()) {
716 files.add(dir);
718 } else {
719 collectMatchedFiles(absoluteRoot, dir, pattern, files);
724 @RegExp
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
737 @RegExp
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);
746 if (ch == '*') {
747 asteriskCount++;
748 continue;
751 final boolean foundRecursivePattern = recursive && asteriskCount == 2 && (ch == '/' || ch == '\\');
752 final boolean asterisksFound = asteriskCount > 0;
754 asteriskCount = 0;
755 recursive = ch == '/' || ch == '\\';
757 if (foundRecursivePattern) {
758 builder.append("(?:[^/]+/)*?");
759 continue;
762 if (asterisksFound){
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);
769 continue;
771 if (ch == '?') {
772 builder.append("[^/]{1}");
773 continue;
775 if (ch == '\\') {
776 builder.append('/');
777 continue;
779 builder.append(ch);
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(".*");
791 else {
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);
813 fromDir.delete();
815 return success;
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)) {
826 result.append(ch);
828 else {
829 result.append("_");
832 else {
838 return result.toString();
841 private static final Method IO_FILE_CAN_EXECUTE_METHOD;
842 static {
843 Method method;
844 try {
845 method = File.class.getDeclaredMethod("canExecute", boolean.class);
847 catch (NoSuchMethodException e) {
848 method = null;
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) {
858 try {
859 return ((Boolean)IO_FILE_CAN_EXECUTE_METHOD.invoke(file));
861 catch (Exception e) {
862 LOG.error(e);
863 return false;
867 //File.setWritable() since java 6.0
868 private static final Method IO_FILE_SET_WRITABLE_METHOD;
869 static {
870 Method method;
871 try {
872 method = File.class.getDeclaredMethod("setWritable", boolean.class);
874 catch (NoSuchMethodException e) {
875 method = null;
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) {
881 try {
882 IO_FILE_SET_WRITABLE_METHOD.invoke(new File(path), !readOnlyStatus);
883 return;
885 catch (IllegalAccessException e) {
886 LOG.error(e);
888 catch (InvocationTargetException e) {
889 LOG.error(e);
892 Process process;
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});
899 try {
900 process.waitFor();
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;
910 static {
911 Method method;
912 try {
913 method = File.class.getDeclaredMethod("setExecutable", boolean.class);
915 catch (NoSuchMethodException e) {
916 method = null;
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) {
930 try {
931 IO_FILE_SET_EXECUTABLE_METHOD.invoke(new File(path), executableFlag);
932 return;
934 catch (IllegalAccessException e) {
935 LOG.error(e);
937 catch (InvocationTargetException e) {
938 LOG.error(e);
941 if (!SystemInfo.isWindows) {
942 // UNIXes go here
943 Process process = Runtime.getRuntime().exec(new String[]{"chmod", executableFlag ? "u+x" : "u-x", path});
944 try {
945 process.waitFor();
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));
959 try {
960 stream.write(text);
962 finally {
963 stream.close();
968 public static boolean processFilesRecursively(final File root, final Processor<File> processor) {
969 final LinkedList<File> queue = new LinkedList<File>();
970 queue.add(root);
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));
981 return true;
984 @Nullable
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;
993 return null;