lossy encoding inspection false positive for transparently converted properties files
[fedora-idea.git] / platform / platform-api / src / com / intellij / openapi / vfs / VfsUtil.java
blobada5ce6b290e9078e2510145178c7440c1dc2b2b
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.
16 package com.intellij.openapi.vfs;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.fileTypes.FileTypeManager;
21 import com.intellij.openapi.fileTypes.FileTypes;
22 import com.intellij.openapi.util.*;
23 import com.intellij.openapi.util.io.FileUtil;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.openapi.vfs.encoding.EncodingManager;
26 import com.intellij.util.ArrayUtil;
27 import com.intellij.util.Function;
28 import com.intellij.util.PathUtil;
29 import com.intellij.util.Processor;
30 import com.intellij.util.containers.Convertor;
31 import com.intellij.util.io.URLUtil;
32 import com.intellij.util.io.fs.FileSystem;
33 import com.intellij.util.io.fs.IFile;
34 import gnu.trove.THashSet;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
39 import java.io.*;
40 import java.net.MalformedURLException;
41 import java.net.URL;
42 import java.nio.charset.Charset;
43 import java.util.*;
45 public class VfsUtil {
46 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.VfsUtil");
48 public static String loadText(@NotNull VirtualFile file) throws IOException{
49 InputStreamReader reader = new InputStreamReader(file.getInputStream(), file.getCharset());
50 try {
51 return new String(FileUtil.loadText(reader, (int)file.getLength()));
53 finally {
54 reader.close();
58 public static void saveText(@NotNull VirtualFile file, @NotNull String text) throws IOException {
59 Charset charset = file.getCharset();
60 file.setBinaryContent(text.getBytes(charset.name()));
63 /**
64 * Checks whether the <code>ancestor {@link VirtualFile}</code> is parent of <code>file
65 * {@link VirtualFile}</code>.
67 * @param ancestor the file
68 * @param file the file
69 * @param strict if <code>false</code> then this method returns <code>true</code> if <code>ancestor</code>
70 * and <code>file</code> are equal
71 * @return <code>true</code> if <code>ancestor</code> is parent of <code>file</code>; <code>false</code> otherwise
73 public static boolean isAncestor(@NotNull VirtualFile ancestor, @NotNull VirtualFile file, boolean strict) {
74 if (!file.getFileSystem().equals(ancestor.getFileSystem())) return false;
75 VirtualFile parent = strict ? file.getParent() : file;
76 while (true) {
77 if (parent == null) return false;
78 if (parent.equals(ancestor)) return true;
79 parent = parent.getParent();
83 /**
84 * Gets the relative path of <code>file</code> to its <code>ancestor</code>. Uses <code>separator</code> for
85 * separating files.
87 * @param file the file
88 * @param ancestor parent file
89 * @param separator character to use as files separator
90 * @return the relative path
92 public static String getRelativePath(@NotNull VirtualFile file, @NotNull VirtualFile ancestor, char separator) {
93 if (!file.getFileSystem().equals(ancestor.getFileSystem())) return null;
95 int length = 0;
96 VirtualFile parent = file;
97 while (true) {
98 if (parent == null) return null;
99 if (parent.equals(ancestor)) break;
100 if (length > 0) {
101 length++;
103 length += parent.getName().length();
104 parent = parent.getParent();
107 char[] chars = new char[length];
108 int index = chars.length;
109 parent = file;
110 while (true) {
111 if (parent.equals(ancestor)) break;
112 if (index < length) {
113 chars[--index] = separator;
115 String name = parent.getName();
116 for (int i = name.length() - 1; i >= 0; i--) {
117 chars[--index] = name.charAt(i);
119 parent = parent.getParent();
121 return new String(chars);
125 * Copies all files matching the <code>filter</code> from <code>fromDir</code> to <code>toDir</code>.
127 * @param requestor any object to control who called this method. Note that
128 * it is considered to be an external change if <code>requestor</code> is <code>null</code>.
129 * See {@link VirtualFileEvent#getRequestor}
130 * @param fromDir the directory to copy from
131 * @param toDir the directory to copy to
132 * @param filter {@link VirtualFileFilter}
133 * @throws IOException if files failed to be copied
135 public static void copyDirectory(Object requestor, @NotNull VirtualFile fromDir, @NotNull VirtualFile toDir, @Nullable VirtualFileFilter filter)
136 throws IOException {
137 VirtualFile[] children = fromDir.getChildren();
138 for (VirtualFile child : children) {
139 if (filter == null || filter.accept(child)) {
140 if (!child.isDirectory()) {
141 copyFile(requestor, child, toDir);
143 else {
144 VirtualFile newChild = toDir.createChildDirectory(requestor, child.getName());
145 copyDirectory(requestor, child, newChild, filter);
152 * Makes a copy of the <code>file</code> in the <code>toDir</code> folder and returns it.
154 * @param requestor any object to control who called this method. Note that
155 * it is considered to be an external change if <code>requestor</code> is <code>null</code>.
156 * See {@link VirtualFileEvent#getRequestor}
157 * @param file file to make a copy of
158 * @param toDir directory to make a copy in
159 * @return a copy of the file
160 * @throws IOException if file failed to be copied
162 public static VirtualFile copyFile(Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile toDir) throws IOException {
163 return copyFile(requestor, file, toDir, file.getName());
167 * Makes a copy of the <code>file</code> in the <code>toDir</code> folder with the <code>newName</code> and returns it.
169 * @param requestor any object to control who called this method. Note that
170 * it is considered to be an external change if <code>requestor</code> is <code>null</code>.
171 * See {@link VirtualFileEvent#getRequestor}
172 * @param file file to make a copy of
173 * @param toDir directory to make a copy in
174 * @param newName new name of the file
175 * @return a copy of the file
176 * @throws IOException if file failed to be copied
178 public static VirtualFile copyFile(Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile toDir, @NotNull @NonNls String newName)
179 throws IOException {
180 final VirtualFile newChild = toDir.createChildData(requestor, newName);
181 // [jeka] TODO: to be duscussed if the copy should have the same timestamp as the original
182 //OutputStream out = newChild.getOutputStream(requestor, -1, file.getActualTimeStamp());
183 newChild.setBinaryContent(file.contentsToByteArray());
184 return newChild;
188 * Copies content of resource to the given file
190 * @param file to copy to
191 * @param resourceUrl url of the resource to be copied
192 * @throws java.io.IOException if resource not found or copying failed
194 public static void copyFromResource(@NotNull VirtualFile file, @NonNls @NotNull String resourceUrl) throws IOException {
195 InputStream out = VfsUtil.class.getResourceAsStream(resourceUrl);
196 if (out == null) {
197 throw new FileNotFoundException(resourceUrl);
199 try {
200 byte[] bytes = FileUtil.adaptiveLoadBytes(out);
201 file.setBinaryContent(bytes);
202 } finally {
203 out.close();
208 * Gets the array of common ancestors for passed files.
210 * @param files array of files
211 * @return array of common ancestors for passed files
213 @NotNull
214 public static VirtualFile[] getCommonAncestors(@NotNull VirtualFile[] files) {
215 // Separate files by first component in the path.
216 HashMap<VirtualFile,Set<VirtualFile>> map = new HashMap<VirtualFile, Set<VirtualFile>>();
217 for (VirtualFile aFile : files) {
218 VirtualFile directory = aFile.isDirectory() ? aFile : aFile.getParent();
219 if (directory == null) return VirtualFile.EMPTY_ARRAY;
220 VirtualFile[] path = getPathComponents(directory);
221 Set<VirtualFile> filesSet;
222 final VirtualFile firstPart = path[0];
223 if (map.containsKey(firstPart)) {
224 filesSet = map.get(firstPart);
226 else {
227 filesSet = new THashSet<VirtualFile>();
228 map.put(firstPart, filesSet);
230 filesSet.add(directory);
232 // Find common ancestor for each set of files.
233 ArrayList<VirtualFile> ancestorsList = new ArrayList<VirtualFile>();
234 for (Set<VirtualFile> filesSet : map.values()) {
235 VirtualFile ancestor = null;
236 for (VirtualFile file : filesSet) {
237 if (ancestor == null) {
238 ancestor = file;
239 continue;
241 ancestor = getCommonAncestor(ancestor, file);
242 //assertTrue(ancestor != null);
244 ancestorsList.add(ancestor);
245 filesSet.clear();
247 return VfsUtil.toVirtualFileArray(ancestorsList);
251 * Gets the common ancestor for passed files, or null if the files do not have common ancestors.
253 * @param file1 fist file
254 * @param file2 second file
255 * @return common ancestor for the passed files. Returns <code>null</code> if
256 * the files do not have common ancestor
258 public static VirtualFile getCommonAncestor(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
259 if (!file1.getFileSystem().equals(file2.getFileSystem())) {
260 return null;
263 VirtualFile[] path1 = getPathComponents(file1);
264 VirtualFile[] path2 = getPathComponents(file2);
266 VirtualFile[] minLengthPath;
267 VirtualFile[] maxLengthPath;
268 if (path1.length < path2.length) {
269 minLengthPath = path1;
270 maxLengthPath = path2;
272 else {
273 minLengthPath = path2;
274 maxLengthPath = path1;
277 int lastEqualIdx = -1;
278 for (int i = 0; i < minLengthPath.length; i++) {
279 if (minLengthPath[i].equals(maxLengthPath[i])) {
280 lastEqualIdx = i;
282 else {
283 break;
286 return lastEqualIdx == -1 ? null : minLengthPath[lastEqualIdx];
290 * Gets an array of files representing paths from root to the passed file.
292 * @param file the file
293 * @return virtual files which represents paths from root to the passed file
295 @NotNull
296 private static VirtualFile[] getPathComponents(@NotNull VirtualFile file) {
297 ArrayList<VirtualFile> componentsList = new ArrayList<VirtualFile>();
298 while (file != null) {
299 componentsList.add(file);
300 file = file.getParent();
302 int size = componentsList.size();
303 VirtualFile[] components = new VirtualFile[size];
304 for (int i = 0; i < size; i++) {
305 components[i] = componentsList.get(size - i - 1);
307 return components;
310 @SuppressWarnings({"HardCodedStringLiteral"})
311 @Nullable
312 public static VirtualFile findRelativeFile(@NotNull String uri, VirtualFile base) {
313 if (base != null) {
314 if (!base.isValid()){
315 LOG.error("Invalid file name: " + base.getName() + ", url: " + uri);
319 uri = uri.replace('\\', '/');
321 if (uri.startsWith("file:///")) {
322 uri = uri.substring("file:///".length());
323 if (!SystemInfo.isWindows) uri = "/" + uri;
325 else if (uri.startsWith("file:/")) {
326 uri = uri.substring("file:/".length());
327 if (!SystemInfo.isWindows) uri = "/" + uri;
329 else if (uri.startsWith("file:")) {
330 uri = uri.substring("file:".length());
333 VirtualFile file = null;
335 if (uri.startsWith("jar:file:/")) {
336 uri = uri.substring("jar:file:/".length());
337 if (!SystemInfo.isWindows) uri = "/" + uri;
338 file = VirtualFileManager.getInstance().findFileByUrl(JarFileSystem.PROTOCOL_PREFIX + uri);
340 else {
341 if (!SystemInfo.isWindows && StringUtil.startsWithChar(uri, '/')) {
342 file = LocalFileSystem.getInstance().findFileByPath(uri);
344 else if (SystemInfo.isWindows && uri.length() >= 2 && Character.isLetter(uri.charAt(0)) && uri.charAt(1) == ':') {
345 file = LocalFileSystem.getInstance().findFileByPath(uri);
349 if (file == null && uri.contains(JarFileSystem.JAR_SEPARATOR)) {
350 file = JarFileSystem.getInstance().findFileByPath(uri);
351 if (file == null && base == null) {
352 file = VirtualFileManager.getInstance().findFileByUrl(uri);
356 if (file == null) {
357 if (base == null) return LocalFileSystem.getInstance().findFileByPath(uri);
358 if (!base.isDirectory()) base = base.getParent();
359 if (base == null) return LocalFileSystem.getInstance().findFileByPath(uri);
360 file = VirtualFileManager.getInstance().findFileByUrl(base.getUrl() + "/" + uri);
361 if (file == null) return null;
364 return file;
367 @NonNls private static final String FILE = "file";
368 @NonNls private static final String JAR = "jar";
369 @NonNls private static final String MAILTO = "mailto";
370 private static final String PROTOCOL_DELIMITER = ":";
373 * Searches for the file specified by given java,net.URL.
374 * Note that this method currently tested only for "file" and "jar" protocols under Unix and Windows
376 * @param url the URL to find file by
377 * @return <code>{@link VirtualFile}</code> if the file was found, <code>null</code> otherwise
379 public static VirtualFile findFileByURL(@NotNull URL url) {
380 VirtualFileManager virtualFileManager = VirtualFileManager.getInstance();
381 return findFileByURL(url, virtualFileManager);
384 public static VirtualFile findFileByURL(@NotNull URL url, @NotNull VirtualFileManager virtualFileManager) {
385 String vfUrl = convertFromUrl(url);
386 return virtualFileManager.findFileByUrl(vfUrl);
390 * Converts VsfUrl info java.net.URL. Does not support "jar:" protocol.
392 * @param vfsUrl VFS url (as constructed by VfsFile.getUrl())
393 * @return converted URL or null if error has occured
396 @Nullable
397 public static URL convertToURL(@NotNull String vfsUrl) {
398 if (vfsUrl.startsWith(JAR)) {
399 LOG.error("jar: protocol not supported.");
400 return null;
403 // [stathik] for supporting mail URLs in Plugin Manager
404 if (vfsUrl.startsWith(MAILTO)) {
405 try {
406 return new URL (vfsUrl);
408 catch (MalformedURLException e) {
409 return null;
413 String[] split = vfsUrl.split("://");
415 if (split.length != 2) {
416 LOG.debug("Malformed VFS URL: " + vfsUrl);
417 return null;
420 String protocol = split[0];
421 String path = split[1];
423 try {
424 if (protocol.equals(FILE)) {
425 return new URL(protocol, "", path);
427 else {
428 return new URL(vfsUrl);
431 catch (MalformedURLException e) {
432 LOG.debug("MalformedURLException occured:" + e.getMessage());
433 return null;
437 @NotNull
438 private static String convertFromUrl(@NotNull URL url) {
439 String protocol = url.getProtocol();
440 String path = url.getPath();
441 if (protocol.equals(JAR)) {
442 if (StringUtil.startsWithConcatenationOf(path, FILE, PROTOCOL_DELIMITER)) {
443 try {
444 URL subURL = new URL(path);
445 path = subURL.getPath();
447 catch (MalformedURLException e) {
448 throw new RuntimeException(VfsBundle.message("url.parse.unhandled.exception"), e);
451 else {
452 throw new RuntimeException(new IOException(VfsBundle.message("url.parse.error", url.toExternalForm())));
455 if (SystemInfo.isWindows || SystemInfo.isOS2) {
456 while (path.length() > 0 && path.charAt(0) == '/') {
457 path = path.substring(1, path.length());
461 path = URLUtil.unescapePercentSequences(path);
462 return protocol + "://" + path;
465 public static String urlToPath(@NonNls String url) {
466 if (url == null) return "";
467 return VirtualFileManager.extractPath(url);
470 @NotNull
471 public static String pathToUrl(@NotNull String path) {
472 return VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, path);
475 @NotNull
476 public static File virtualToIoFile(@NotNull VirtualFile file) {
477 return new File(PathUtil.toPresentableUrl(file.getUrl()));
480 @NotNull
481 public static IFile virtualToIFile(@NotNull VirtualFile file) {
482 return FileSystem.FILE_SYSTEM.createFile(PathUtil.toPresentableUrl(file.getUrl()));
485 public static VirtualFile copyFileRelative(Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile toDir, @NotNull String relativePath) throws IOException {
486 StringTokenizer tokenizer = new StringTokenizer(relativePath,"/");
487 VirtualFile curDir = toDir;
489 while (true) {
490 String token = tokenizer.nextToken();
491 if (tokenizer.hasMoreTokens()) {
492 VirtualFile childDir = curDir.findChild(token);
493 if (childDir == null) {
494 childDir = curDir.createChildDirectory(requestor, token);
496 curDir = childDir;
498 else {
499 return copyFile(requestor, file, curDir, token);
504 @NotNull
505 public static String fixIDEAUrl(@NotNull String ideaUrl ) {
506 int idx = ideaUrl.indexOf("://");
507 if( idx >= 0 ) {
508 String s = ideaUrl.substring(0, idx);
510 if (s.equals(JarFileSystem.PROTOCOL)) {
511 //noinspection HardCodedStringLiteral
512 s = "jar:file";
514 ideaUrl = s+":/"+ideaUrl.substring(idx+3);
516 return ideaUrl;
519 @NotNull
520 public static String fixURLforIDEA(@NotNull String url ) {
521 int idx = url.indexOf(":/");
522 if( idx >= 0 && idx+2 < url.length() && url.charAt(idx+2) != '/' ) {
523 String prefix = url.substring(0, idx);
524 String suffix = url.substring(idx+2);
526 if (SystemInfo.isWindows) {
527 url = prefix+"://"+suffix;
528 } else {
529 url = prefix+":///"+suffix;
532 return url;
535 public static boolean isAncestor(@NotNull File ancestor, @NotNull File file, boolean strict) {
536 File parent = strict ? file.getParentFile() : file;
537 while (parent != null) {
538 if (parent.equals(ancestor)) return true;
539 parent = parent.getParentFile();
542 return false;
546 * Returns the relative path from one virtual file to another.
548 * @param src the file from which the relative path is built.
549 * @param dst the file to which the path is built.
550 * @param separatorChar the separator for the path components.
551 * @return the relative path, or null if the files have no common ancestor.
552 * @since 5.0.2
555 @Nullable
556 public static String getPath(@NotNull VirtualFile src, @NotNull VirtualFile dst, char separatorChar) {
557 final VirtualFile commonAncestor = getCommonAncestor(src, dst);
558 if (commonAncestor != null) {
559 StringBuilder buffer = new StringBuilder();
560 if (src != commonAncestor) {
561 while (src.getParent() != commonAncestor) {
562 buffer.append("..").append(separatorChar);
563 src = src.getParent();
564 assert src != null;
567 buffer.append(getRelativePath(dst, commonAncestor, separatorChar));
568 return buffer.toString();
571 return null;
574 public static boolean isValidName(@NotNull String name) {
575 return name.indexOf('\\') < 0 && name.indexOf('/') < 0;
578 public static String getUrlForLibraryRoot(@NotNull File libraryRoot) {
579 String path = FileUtil.toSystemIndependentName(libraryRoot.getAbsolutePath());
580 if (FileTypeManager.getInstance().getFileTypeByFileName(libraryRoot.getName()) == FileTypes.ARCHIVE) {
581 return VirtualFileManager.constructUrl(JarFileSystem.getInstance().getProtocol(), path + JarFileSystem.JAR_SEPARATOR);
583 else {
584 return VirtualFileManager.constructUrl(LocalFileSystem.getInstance().getProtocol(), path);
588 public static VirtualFile createChildSequent(Object requestor, @NotNull VirtualFile dir, @NotNull String prefix, @NotNull String extension) throws IOException {
589 String fileName = prefix + "." + extension;
590 int i = 1;
591 while (dir.findChild(fileName) != null) {
592 fileName = prefix + i + "." + extension;
593 i++;
595 return dir.createChildData(requestor, fileName);
598 @NotNull
599 public static String[] filterNames(@NotNull String[] names) {
600 int filteredCount = 0;
601 for (String string : names) {
602 if (isBadName(string)) filteredCount++;
604 if (filteredCount == 0) return names;
606 String[] result = ArrayUtil.newStringArray(names.length - filteredCount);
607 int count = 0;
608 for (String string : names) {
609 if (isBadName(string)) continue;
610 result[count++] = string;
613 return result;
616 public static boolean isBadName(String name) {
617 return name == null || name.length() == 0 || "/".equals(name) || "\\".equals(name);
620 public static VirtualFile createDirectories(@NotNull final String dir) throws IOException {
621 final Ref<IOException> err = new Ref<IOException>();
622 VirtualFile result = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
623 public VirtualFile compute() {
624 try {
625 return createDirectoryIfMissing(dir);
627 catch (IOException e) {
628 err.set(e);
629 return null;
633 if (!err.isNull()) throw err.get();
634 return result;
637 @Nullable
638 public static VirtualFile createDirectoryIfMissing(@NotNull String dir) throws IOException {
639 return doCreateDirectoriesIfMissing(FileUtil.toSystemIndependentName(dir));
642 private static VirtualFile doCreateDirectoriesIfMissing(String dir) throws IOException {
643 final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(dir);
644 if (file == null) {
645 int pos = dir.lastIndexOf('/');
646 if (pos < 0) return null;
647 VirtualFile parent = createDirectoryIfMissing(dir.substring(0, pos));
648 if (parent == null) return null;
649 final String dirName = dir.substring(pos + 1);
650 return parent.createChildDirectory(LocalFileSystem.getInstance(), dirName);
652 return file;
655 public static <E extends Throwable> VirtualFile doActionAndRestoreEncoding(@NotNull VirtualFile fileBefore, @NotNull ThrowableComputable<VirtualFile, E> action) throws E {
656 Charset charsetBefore = EncodingManager.getInstance().getEncoding(fileBefore, true);
657 VirtualFile fileAfter = null;
658 try {
659 fileAfter = action.compute();
660 return fileAfter;
662 finally {
663 if (fileAfter != null) {
664 Charset actual = EncodingManager.getInstance().getEncoding(fileAfter, true);
665 if (!Comparing.equal(actual, charsetBefore)) {
666 EncodingManager.getInstance().setEncoding(fileAfter, charsetBefore);
672 public static void processFileRecursivelyWithoutIgnored(@NotNull final VirtualFile root, @NotNull final Processor<VirtualFile> processor) {
673 final FileTypeManager ftm = FileTypeManager.getInstance();
674 processFilesRecursively(root, processor, new Convertor<VirtualFile, Boolean>() {
675 public Boolean convert(final VirtualFile vf) {
676 return ! ftm.isFileIgnored(vf.getName());
681 public static void processFilesRecursively(@NotNull VirtualFile root, @NotNull Processor<VirtualFile> processor,
682 @NotNull Convertor<VirtualFile, Boolean> directoryFilter) {
683 final LinkedList<VirtualFile> queue = new LinkedList<VirtualFile>();
684 queue.add(root);
685 while (!queue.isEmpty()) {
686 final VirtualFile file = queue.removeFirst();
687 if (!processor.process(file)) return;
688 if (file.isDirectory() && directoryFilter.convert(file)) {
689 queue.addAll(Arrays.asList(file.getChildren()));
694 public static boolean processFilesRecursively(@NotNull VirtualFile root, @NotNull Processor<VirtualFile> processor) {
695 final LinkedList<VirtualFile> queue = new LinkedList<VirtualFile>();
696 queue.add(root);
697 while (!queue.isEmpty()) {
698 final VirtualFile file = queue.removeFirst();
699 if (!processor.process(file)) return false;
700 if (file.isDirectory()) {
701 queue.addAll(Arrays.asList(file.getChildren()));
704 return true;
707 @Nullable
708 public static <T> T processInputStream(@NotNull final VirtualFile file, @NotNull Function<InputStream, T> function) {
709 InputStream stream = null;
710 try {
711 stream = file.getInputStream();
712 return function.fun(stream);
714 catch (IOException e) {
715 LOG.error(e);
716 } finally {
717 try {
718 if (stream != null) {
719 stream.close();
722 catch (IOException e) {
723 LOG.error(e);
726 return null;
729 @NotNull
730 public static VirtualFile[] toVirtualFileArray(@NotNull Collection<? extends VirtualFile> files) {
731 int size = files.size();
732 if (size == 0) return VirtualFile.EMPTY_ARRAY;
733 return files.toArray(new VirtualFile[size]);