fix starting Tomcat: do not skip .keystore files from conf dir
[fedora-idea.git] / platform / util / src / com / intellij / openapi / util / io / FileUtil.java
blob0ff6f7cd39520f300e3100d06ea7e531d9aa9ad1
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];
49 //private static final byte[] BUFFER = new byte[1024 * 20];
51 @Nullable
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;
74 int len = 0;
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;
82 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();
99 /**
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;
111 while (true) {
112 if (parent == null) {
113 return false;
115 if (parent.equals(ancestor)) {
116 return true;
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.
130 @Nullable
131 public static File getParentFile(final File file) {
132 int skipCount = 0;
133 File parentFile = file;
134 while (true) {
135 parentFile = parentFile.getParentFile();
136 if (parentFile == null) {
137 return null;
139 if (".".equals(parentFile.getName())) {
140 continue;
142 if ("..".equals(parentFile.getName())) {
143 skipCount++;
144 continue;
146 if (skipCount > 0) {
147 skipCount--;
148 continue;
150 return parentFile;
154 @NotNull
155 public static char[] loadFileText(File file) throws IOException {
156 return loadFileText(file, null);
159 @NotNull
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);
163 try{
164 return loadText(reader, (int)file.length());
166 finally{
167 reader.close();
171 @NotNull
172 public static char[] loadText(Reader reader, int length) throws IOException {
173 char[] chars = new char[length];
174 int count = 0;
175 while (count < chars.length) {
176 int n = reader.read(chars, count, chars.length - count);
177 if (n <= 0) break;
178 count += n;
180 if (count == chars.length){
181 return chars;
183 else{
184 char[] newChars = new char[count];
185 System.arraycopy(chars, 0, newChars, 0, count);
186 return newChars;
190 @NotNull
191 public static byte[] loadFileBytes(File file) throws IOException {
192 byte[] bytes;
193 final InputStream stream = new FileInputStream(file);
194 try{
195 final long len = file.length();
196 if (len < 0) {
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);
206 finally{
207 stream.close();
209 return bytes;
212 @NotNull
213 public static byte[] loadBytes(InputStream stream, int length) throws IOException{
214 byte[] bytes = new byte[length];
215 int count = 0;
216 while(count < length) {
217 int n = stream.read(bytes, count, length - count);
218 if (n <= 0) break;
219 count += n;
221 return bytes;
224 @NotNull
225 public static byte[] loadBytes(InputStream stream) throws IOException{
226 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
227 final byte[] bytes = BUFFER.get();
228 while(true) {
229 int n = stream.read(bytes, 0, bytes.length);
230 if (n <= 0) break;
231 buffer.write(bytes, 0, n);
233 buffer.close();
234 return buffer.toByteArray();
237 @NotNull
238 public static String loadTextAndClose(Reader reader) throws IOException {
239 try {
240 return new String(adaptiveLoadText(reader));
242 finally {
243 reader.close();
247 @NotNull
248 public static char[] adaptiveLoadText(Reader reader) throws IOException {
249 char[] chars = new char[4096];
250 List<char[]> buffers = null;
251 int count = 0;
252 int total = 0;
253 while (true) {
254 int n = reader.read(chars, count, chars.length-count);
255 if (n <= 0) break;
256 count += n;
257 if (total > 1024*1024*10) throw new FileTooBigException("File too big "+reader);
258 total += n;
259 if (count == chars.length) {
260 if (buffers == null) {
261 buffers = new ArrayList<char[]>();
263 buffers.add(chars);
264 int newLength = Math.min(1024*1024, chars.length * 2);
265 chars = new char[newLength];
266 count = 0;
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);
277 return result;
280 @NotNull
281 public static byte[] adaptiveLoadBytes(InputStream stream) throws IOException{
282 byte[] bytes = new byte[4096];
283 List<byte[]> buffers = null;
284 int count = 0;
285 int total = 0;
286 while (true) {
287 int n = stream.read(bytes, count, bytes.length-count);
288 if (n <= 0) break;
289 count += n;
290 if (total > 1024*1024*10) throw new FileTooBigException("File too big "+stream);
291 total += n;
292 if (count == bytes.length) {
293 if (buffers == null) {
294 buffers = new ArrayList<byte[]>();
296 buffers.add(bytes);
297 int newLength = Math.min(1024*1024, bytes.length * 2);
298 bytes = new byte[newLength];
299 count = 0;
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);
310 return result;
313 public static File createTempDirectory(@NonNls String prefix, @NonNls String suffix) throws IOException{
314 File file = doCreateTempFile(prefix, suffix);
315 file.delete();
316 file.mkdir();
317 return file;
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);
322 file.delete();
323 if (create) {
324 file.createNewFile();
326 return file;
329 public static File createTempFile(@NonNls String prefix, @NonNls String suffix) throws IOException{
330 File file = doCreateTempFile(prefix, suffix);
331 file.delete();
332 file.createNewFile();
333 return file;
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;
346 while(true){
347 try{
348 return File.createTempFile(prefix, suffix, dir).getCanonicalFile();
350 catch(IOException e){ // Win32 createFileExclusively access denied
351 if (++exceptionsCount >= 100) {
352 throw e;
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) {
365 return;
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() {
385 public void run() {
386 final Thread currentThread = Thread.currentThread();
387 currentThread.setPriority(Thread.MIN_PRIORITY);
388 ShutDownTracker.getInstance().registerStopperThread(currentThread);
389 try {
390 for (File tempFile : tempFiles) {
391 delete(tempFile);
394 finally {
395 ShutDownTracker.getInstance().unregisterStopperThread(currentThread);
396 currentThread.setPriority(Thread.NORM_PRIORITY);
401 try {
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");
412 t.start();
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);
425 if (isSameDrive) {
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)) {
430 return tempFile;
434 delete(file);
436 return null;
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();
450 if (files != null) {
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;
458 try {
459 Thread.sleep(10);
461 catch (InterruptedException ignored) {
465 return false;
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();
476 return false;
479 public static boolean createIfDoesntExist(File file) {
480 if (file.exists()) return true;
481 try {
482 if (!createParentDirs(file)) return false;
484 OutputStream s = new FileOutputStream(file);
485 s.close();
486 return true;
488 catch (IOException e) {
489 return false;
493 public static boolean ensureCanCreateFile(File file) {
494 if (file.exists()) return file.canWrite();
495 if (!createIfDoesntExist(file)) return false;
496 return delete(file);
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;
509 try {
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);
517 throw ioException;
519 createParentDirs(toFile);
520 fos = new FileOutputStream(toFile);
523 if (Patches.FILE_CHANNEL_TRANSFER_BROKEN) {
524 FileInputStream fis = new FileInputStream(fromFile);
525 try {
526 copy(fis, fos);
528 finally {
529 fis.close();
530 fos.close();
533 else {
534 FileChannel fromChannel = new FileInputStream(fromFile).getChannel();
535 FileChannel toChannel = fos.getChannel();
536 try {
537 fromChannel.transferTo(0, Long.MAX_VALUE, toChannel);
539 finally {
540 fromChannel.close();
541 toChannel.close();
545 if (syncTimestamp) {
546 toFile.setLastModified(fromFile.lastModified());
550 public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
551 final byte[] buffer = BUFFER.get();
552 while (true) {
553 int read = inputStream.read(buffer);
554 if (read < 0) break;
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();
561 int toRead = size;
562 while (toRead > 0) {
563 int read = inputStream.read(buffer, 0, Math.min(buffer.length, toRead));
564 if (read < 0) break;
565 toRead -= read;
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 {
583 toDir.mkdirs();
584 if (isAncestor(fromDir, toDir, true)) {
585 LOG.error(fromDir.getAbsolutePath() + " is ancestor of " + toDir + ". Can't copy to itself.");
586 return;
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)) {
593 continue;
595 if (file.isDirectory()) {
596 copyDir(file, new File(toDir, file.getName()), filter);
598 else {
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('.');
610 if (i != -1) {
611 name = name.substring(0, i);
613 return name;
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) {
621 int postfix = 0;
622 String ext = 0 == aExtension.length() ? "" : "." + aExtension;
624 File candidate = new File(aParentFolder, aFilePrefix + ext);
625 while (candidate.exists()) {
626 postfix++;
627 candidate = new File(aParentFolder, aFilePrefix + Integer.toString(postfix) + ext);
629 return candidate;
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) {
646 do {
647 if (fileFilter != null && !fileFilter.accept(file)) return false;
648 file = file.getParentFile();
650 while (file != null);
651 return true;
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);
659 delete(source);
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);
674 char next1;
675 if (last2 == '/' || last2 == File.separatorChar) {
676 next1 = path1.charAt(length2 -1);
678 else {
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();
696 @NotNull
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();
703 @NotNull
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();
709 return path;
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) {
720 if (dir.isFile()) {
721 final String path = toSystemIndependentName(getRelativePath(absoluteRoot, dir));
722 if (pattern.matcher(path).matches()) {
723 files.add(dir);
725 } else {
726 collectMatchedFiles(absoluteRoot, dir, pattern, files);
731 @RegExp
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
744 @RegExp
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);
753 if (ch == '*') {
754 asteriskCount++;
755 continue;
758 final boolean foundRecursivePattern = recursive && asteriskCount == 2 && (ch == '/' || ch == '\\');
759 final boolean asterisksFound = asteriskCount > 0;
761 asteriskCount = 0;
762 recursive = ch == '/' || ch == '\\';
764 if (foundRecursivePattern) {
765 builder.append("(?:[^/]+/)*?");
766 continue;
769 if (asterisksFound){
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);
776 continue;
778 if (ch == '?') {
779 builder.append("[^/]{1}");
780 continue;
782 if (ch == '\\') {
783 builder.append('/');
784 continue;
786 builder.append(ch);
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(".*");
798 else {
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);
820 fromDir.delete();
822 return success;
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)) {
833 result.append(ch);
835 else {
836 result.append("_");
839 else {
845 return result.toString();
848 private static final Method IO_FILE_CAN_EXECUTE_METHOD;
849 static {
850 Method method;
851 try {
852 method = File.class.getDeclaredMethod("canExecute", boolean.class);
854 catch (NoSuchMethodException e) {
855 method = null;
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) {
865 try {
866 return ((Boolean)IO_FILE_CAN_EXECUTE_METHOD.invoke(file));
868 catch (Exception e) {
869 LOG.error(e);
870 return false;
874 //File.setWritable() since java 6.0
875 private static final Method IO_FILE_SET_WRITABLE_METHOD;
876 static {
877 Method method;
878 try {
879 method = File.class.getDeclaredMethod("setWritable", boolean.class);
881 catch (NoSuchMethodException e) {
882 method = null;
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) {
888 try {
889 IO_FILE_SET_WRITABLE_METHOD.invoke(new File(path), !readOnlyStatus);
890 return;
892 catch (IllegalAccessException e) {
893 LOG.error(e);
895 catch (InvocationTargetException e) {
896 LOG.error(e);
899 Process process;
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});
906 try {
907 process.waitFor();
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;
917 static {
918 Method method;
919 try {
920 method = File.class.getDeclaredMethod("setExecutable", boolean.class);
922 catch (NoSuchMethodException e) {
923 method = null;
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) {
937 try {
938 IO_FILE_SET_EXECUTABLE_METHOD.invoke(new File(path), executableFlag);
939 return;
941 catch (IllegalAccessException e) {
942 LOG.error(e);
944 catch (InvocationTargetException e) {
945 LOG.error(e);
948 if (!SystemInfo.isWindows) {
949 // UNIXes go here
950 Process process = Runtime.getRuntime().exec(new String[]{"chmod", executableFlag ? "u+x" : "u-x", path});
951 try {
952 process.waitFor();
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));
966 try {
967 stream.write(text);
969 finally {
970 stream.close();
975 public static boolean processFilesRecursively(final File root, final Processor<File> processor) {
976 final LinkedList<File> queue = new LinkedList<File>();
977 queue.add(root);
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));
988 return true;
991 @Nullable
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;
1000 return null;