/** * Gets the array of common ancestors for passed files. * * @param files array of files * @return array of common ancestors for passed files */ @NotNull public static VirtualFile[] getCommonAncestors(@NotNull VirtualFile[] files) { // Separate files by first component in the path. HashMap<VirtualFile, Set<VirtualFile>> map = new HashMap<VirtualFile, Set<VirtualFile>>(); for (VirtualFile aFile : files) { VirtualFile directory = aFile.isDirectory() ? aFile : aFile.getParent(); if (directory == null) return VirtualFile.EMPTY_ARRAY; VirtualFile[] path = getPathComponents(directory); Set<VirtualFile> filesSet; final VirtualFile firstPart = path[0]; if (map.containsKey(firstPart)) { filesSet = map.get(firstPart); } else { filesSet = new THashSet<VirtualFile>(); map.put(firstPart, filesSet); } filesSet.add(directory); } // Find common ancestor for each set of files. ArrayList<VirtualFile> ancestorsList = new ArrayList<VirtualFile>(); for (Set<VirtualFile> filesSet : map.values()) { VirtualFile ancestor = null; for (VirtualFile file : filesSet) { if (ancestor == null) { ancestor = file; continue; } ancestor = getCommonAncestor(ancestor, file); // assertTrue(ancestor != null); } ancestorsList.add(ancestor); filesSet.clear(); } return toVirtualFileArray(ancestorsList); }
private void applyChildrenChangeEvents(VirtualFile parent, List<VFileEvent> events) { final NewVirtualFileSystem delegate = getDelegate(parent); TIntArrayList childrenIdsUpdated = new TIntArrayList(); List<VirtualFile> childrenToBeUpdated = new SmartList<VirtualFile>(); assert parent != null && parent != mySuperRoot; final int parentId = getFileId(parent); assert parentId != 0; TIntHashSet parentChildrenIds = new TIntHashSet(FSRecords.list(parentId)); boolean hasRemovedChildren = false; for (VFileEvent event : events) { if (event instanceof VFileCreateEvent) { String name = ((VFileCreateEvent) event).getChildName(); final VirtualFile fake = new FakeVirtualFile(parent, name); final FileAttributes attributes = delegate.getAttributes(fake); if (attributes != null) { final int childId = createAndFillRecord(delegate, fake, parentId, attributes); assert parent instanceof VirtualDirectoryImpl : parent; final VirtualDirectoryImpl dir = (VirtualDirectoryImpl) parent; VirtualFileSystemEntry child = dir.createChild(name, childId, dir.getFileSystem()); childrenToBeUpdated.add(child); childrenIdsUpdated.add(childId); parentChildrenIds.add(childId); } } else if (event instanceof VFileDeleteEvent) { VirtualFile file = ((VFileDeleteEvent) event).getFile(); if (!file.exists()) { LOG.error("Deleting a file, which does not exist: " + file.getPath()); continue; } hasRemovedChildren = true; int id = getFileId(file); childrenToBeUpdated.add(file); childrenIdsUpdated.add(-id); parentChildrenIds.remove(id); } } FSRecords.updateList(parentId, parentChildrenIds.toArray()); if (hasRemovedChildren) clearIdCache(); VirtualDirectoryImpl parentImpl = (VirtualDirectoryImpl) parent; for (int i = 0, len = childrenIdsUpdated.size(); i < len; ++i) { final int childId = childrenIdsUpdated.get(i); final VirtualFile childFile = childrenToBeUpdated.get(i); if (childId > 0) { parentImpl.addChild((VirtualFileSystemEntry) childFile); } else { FSRecords.deleteRecordRecursively(-childId); parentImpl.removeChild(childFile); invalidateSubtree(childFile); } } }
@Nullable private XmlFile createAnnotationsXml( @NotNull VirtualFile root, @NonNls @NotNull String packageName) { final String[] dirs = packageName.split("[\\.]"); for (String dir : dirs) { if (dir.isEmpty()) break; VirtualFile subdir = root.findChild(dir); if (subdir == null) { try { subdir = root.createChildDirectory(null, dir); } catch (IOException e) { LOG.error(e); } } root = subdir; } final PsiDirectory directory = myPsiManager.findDirectory(root); if (directory == null) return null; final PsiFile psiFile = directory.findFile(ANNOTATIONS_XML); if (psiFile instanceof XmlFile) { return (XmlFile) psiFile; } try { final PsiFileFactory factory = PsiFileFactory.getInstance(myPsiManager.getProject()); return (XmlFile) directory.add( factory.createFileFromText(ANNOTATIONS_XML, XmlFileType.INSTANCE, "<root></root>")); } catch (IncorrectOperationException e) { LOG.error(e); } return null; }
/** * Makes a copy of the <code>file</code> in the <code>toDir</code> folder and returns it. Handles * both files and directories. * * @param requestor any object to control who called this method. Note that it is considered to be * an external change if <code>requestor</code> is <code>null</code>. See {@link * VirtualFileEvent#getRequestor} * @param file file or directory to make a copy of * @param toDir directory to make a copy in * @return a copy of the file * @throws IOException if file failed to be copied */ public static VirtualFile copy( Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile toDir) throws IOException { if (file.isDirectory()) { VirtualFile newDir = toDir.createChildDirectory(requestor, file.getName()); copyDirectory(requestor, file, newDir, null); return newDir; } else { return copyFile(requestor, file, toDir); } }
@NotNull private static VirtualFile[] filterByReadOnliness(@NotNull VirtualFile[] files) { List<VirtualFile> result = new ArrayList<VirtualFile>(); for (VirtualFile file : files) { if (file.isInLocalFileSystem()) { result.add(file); } } return VfsUtilCore.toVirtualFileArray(result); }
@Override protected char[] appendPathOnFileSystem(int accumulatedPathLength, int[] positionRef) { String parentPath = myParentLocalFile.getPath(); char[] chars = new char [parentPath.length() + JarFileSystem.JAR_SEPARATOR.length() + accumulatedPathLength]; positionRef[0] = copyString(chars, positionRef[0], myParentLocalFile.getPath()); positionRef[0] = copyString(chars, positionRef[0], JarFileSystem.JAR_SEPARATOR); return chars; }
@SuppressWarnings({"HardCodedStringLiteral"}) @Nullable public static VirtualFile findRelativeFile(@NotNull String uri, @Nullable VirtualFile base) { if (base != null) { if (!base.isValid()) { LOG.error("Invalid file name: " + base.getName() + ", url: " + uri); } } uri = uri.replace('\\', '/'); if (uri.startsWith("file:///")) { uri = uri.substring("file:///".length()); if (!SystemInfo.isWindows) uri = "/" + uri; } else if (uri.startsWith("file:/")) { uri = uri.substring("file:/".length()); if (!SystemInfo.isWindows) uri = "/" + uri; } else if (uri.startsWith("file:")) { uri = uri.substring("file:".length()); } VirtualFile file = null; if (uri.startsWith("jar:file:/")) { uri = uri.substring("jar:file:/".length()); if (!SystemInfo.isWindows) uri = "/" + uri; file = VirtualFileManager.getInstance().findFileByUrl(JarFileSystem.PROTOCOL_PREFIX + uri); } else { if (!SystemInfo.isWindows && StringUtil.startsWithChar(uri, '/')) { file = LocalFileSystem.getInstance().findFileByPath(uri); } else if (SystemInfo.isWindows && uri.length() >= 2 && Character.isLetter(uri.charAt(0)) && uri.charAt(1) == ':') { file = LocalFileSystem.getInstance().findFileByPath(uri); } } if (file == null && uri.contains(JarFileSystem.JAR_SEPARATOR)) { file = JarFileSystem.getInstance().findFileByPath(uri); if (file == null && base == null) { file = VirtualFileManager.getInstance().findFileByUrl(uri); } } if (file == null) { if (base == null) return LocalFileSystem.getInstance().findFileByPath(uri); if (!base.isDirectory()) base = base.getParent(); if (base == null) return LocalFileSystem.getInstance().findFileByPath(uri); file = VirtualFileManager.getInstance().findFileByUrl(base.getUrl() + "/" + uri); if (file == null) return null; } return file; }
@NotNull public static String getReadableUrl(@NotNull final VirtualFile file) { String url = null; if (file.isInLocalFileSystem()) { url = file.getPresentableUrl(); } if (url == null) { url = file.getUrl(); } return url; }
public static VirtualFile createDirectoryIfMissing(VirtualFile parent, String relativePath) throws IOException { for (String each : StringUtil.split(relativePath, "/")) { VirtualFile child = parent.findChild(each); if (child == null) { child = parent.createChildDirectory(LocalFileSystem.getInstance(), each); } parent = child; } return parent; }
public static VirtualFile createChildSequent( Object requestor, @NotNull VirtualFile dir, @NotNull String prefix, @NotNull String extension) throws IOException { String fileName = prefix + "." + extension; int i = 1; while (dir.findChild(fileName) != null) { fileName = prefix + i + "." + extension; i++; } return dir.createChildData(requestor, fileName); }
@Override public VirtualFile createChildFile( Object requestor, @NotNull VirtualFile parent, @NotNull String file) throws IOException { getDelegate(parent).createChildFile(requestor, parent, file); processEvent(new VFileCreateEvent(requestor, parent, file, false, false)); final VirtualFile child = parent.findChild(file); if (child == null) { throw new IOException("Cannot create child file '" + file + "' at " + parent.getPath()); } return child; }
@Override public VirtualFile createChildDirectory( Object requestor, @NotNull VirtualFile parent, @NotNull String dir) throws IOException { getDelegate(parent).createChildDirectory(requestor, parent, dir); processEvent(new VFileCreateEvent(requestor, parent, dir, true, false)); final VirtualFile child = parent.findChild(dir); if (child == null) { throw new IOException("Cannot create child directory '" + dir + "' at " + parent.getPath()); } return child; }
private void executeMove(@NotNull VirtualFile file, @NotNull VirtualFile newParent) { clearIdCache(); final int fileId = getFileId(file); final int newParentId = getFileId(newParent); final int oldParentId = getFileId(file.getParent()); removeIdFromParentList(oldParentId, fileId, file.getParent(), file); FSRecords.setParent(fileId, newParentId); appendIdToParentList(newParentId, fileId); ((VirtualFileSystemEntry) file).setParent(newParent); }
private static VirtualFile doCreateDirectoriesIfMissing(String dir) throws IOException { final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(dir); if (file == null) { int pos = dir.lastIndexOf('/'); if (pos < 0) return null; VirtualFile parent = createDirectoryIfMissing(dir.substring(0, pos)); if (parent == null) return null; final String dirName = dir.substring(pos + 1); return parent.createChildDirectory(LocalFileSystem.getInstance(), dirName); } return file; }
@Nullable public static VirtualFile findRelativeFile(@Nullable VirtualFile base, String... path) { VirtualFile file = base; for (String pathElement : path) { if (file == null) return null; if ("..".equals(pathElement)) { file = file.getParent(); } else { file = file.findChild(pathElement); } } return file; }
private void appendChosenAnnotationsRoot( @NotNull final OrderEntry entry, @NotNull final VirtualFile vFile) { if (entry instanceof LibraryOrderEntry) { Library library = ((LibraryOrderEntry) entry).getLibrary(); LOG.assertTrue(library != null); final ModifiableRootModel rootModel = ModuleRootManager.getInstance(entry.getOwnerModule()).getModifiableModel(); final Library.ModifiableModel model = library.getModifiableModel(); model.addRoot(vFile, AnnotationOrderRootType.getInstance()); model.commit(); rootModel.commit(); } else if (entry instanceof ModuleSourceOrderEntry) { final ModifiableRootModel model = ModuleRootManager.getInstance(entry.getOwnerModule()).getModifiableModel(); final JavaModuleExternalPaths extension = model.getModuleExtension(JavaModuleExternalPaths.class); extension.setExternalAnnotationUrls( ArrayUtil.mergeArrays(extension.getExternalAnnotationsUrls(), vFile.getUrl())); model.commit(); } else if (entry instanceof JdkOrderEntry) { final SdkModificator sdkModificator = ((JdkOrderEntry) entry).getJdk().getSdkModificator(); sdkModificator.addRoot(vFile, AnnotationOrderRootType.getInstance()); sdkModificator.commitChanges(); } myExternalAnnotations.clear(); }
@NotNull private static List<VFileEvent> validateEvents(@NotNull List<VFileEvent> events) { final List<EventWrapper> deletionEvents = ContainerUtil.newArrayList(); for (int i = 0, size = events.size(); i < size; i++) { final VFileEvent event = events.get(i); if (event instanceof VFileDeleteEvent && event.isValid()) { deletionEvents.add(new EventWrapper((VFileDeleteEvent) event, i)); } } final TIntHashSet invalidIDs; if (deletionEvents.isEmpty()) { invalidIDs = EmptyIntHashSet.INSTANCE; } else { ContainerUtil.quickSort(deletionEvents, DEPTH_COMPARATOR); invalidIDs = new TIntHashSet(deletionEvents.size()); final Set<VirtualFile> dirsToBeDeleted = new THashSet<VirtualFile>(deletionEvents.size()); nextEvent: for (EventWrapper wrapper : deletionEvents) { final VirtualFile candidate = wrapper.event.getFile(); VirtualFile parent = candidate; while (parent != null) { if (dirsToBeDeleted.contains(parent)) { invalidIDs.add(wrapper.id); continue nextEvent; } parent = parent.getParent(); } if (candidate.isDirectory()) { dirsToBeDeleted.add(candidate); } } } final List<VFileEvent> filtered = new ArrayList<VFileEvent>(events.size() - invalidIDs.size()); for (int i = 0, size = events.size(); i < size; i++) { final VFileEvent event = events.get(i); if (event.isValid() && !(event instanceof VFileDeleteEvent && invalidIDs.contains(i))) { filtered.add(event); } } return filtered; }
/** * Returns the relative path from one virtual file to another. * * @param src the file from which the relative path is built. * @param dst the file to which the path is built. * @param separatorChar the separator for the path components. * @return the relative path, or null if the files have no common ancestor. * @since 5.0.2 */ @Nullable public static String getPath( @NotNull VirtualFile src, @NotNull VirtualFile dst, char separatorChar) { final VirtualFile commonAncestor = getCommonAncestor(src, dst); if (commonAncestor != null) { StringBuilder buffer = new StringBuilder(); if (src != commonAncestor) { while (src.getParent() != commonAncestor) { buffer.append("..").append(separatorChar); src = src.getParent(); } } buffer.append(getRelativePath(dst, commonAncestor, separatorChar)); return buffer.toString(); } return null; }
@Override public void renameFile( final Object requestor, @NotNull final VirtualFile file, @NotNull final String newName) throws IOException { getDelegate(file).renameFile(requestor, file, newName); processEvent( new VFilePropertyChangeEvent( requestor, file, VirtualFile.PROP_NAME, file.getName(), newName, false)); }
@Override @NotNull public byte[] contentsToByteArray(@NotNull final VirtualFile file, boolean cacheContent) throws IOException { InputStream contentStream = null; boolean reloadFromDelegate; boolean outdated; int fileId; synchronized (myInputLock) { fileId = getFileId(file); outdated = checkFlag(fileId, MUST_RELOAD_CONTENT) || FSRecords.getLength(fileId) == -1L; reloadFromDelegate = outdated || (contentStream = readContent(file)) == null; } if (reloadFromDelegate) { final NewVirtualFileSystem delegate = getDelegate(file); final byte[] content; if (outdated) { // in this case, file can have out-of-date length. so, update it first (it's needed for // correct contentsToByteArray() work) // see IDEA-90813 for possible bugs FSRecords.setLength(fileId, delegate.getLength(file)); content = delegate.contentsToByteArray(file); } else { // a bit of optimization content = delegate.contentsToByteArray(file); FSRecords.setLength(fileId, content.length); } ApplicationEx application = (ApplicationEx) ApplicationManager.getApplication(); // we should cache every local files content // because the local history feature is currently depends on this cache, // perforce offline mode as well if ((!delegate.isReadOnly() || // do not cache archive content unless asked cacheContent && !application.isInternal() && !application.isUnitTestMode()) && content.length <= PersistentFSConstants.FILE_LENGTH_TO_CACHE_THRESHOLD) { synchronized (myInputLock) { writeContent(file, new ByteSequence(content), delegate.isReadOnly()); setFlag(file, MUST_RELOAD_CONTENT, false); } } return content; } else { try { final int length = (int) file.getLength(); assert length >= 0 : file; return FileUtil.loadBytes(contentStream, length); } catch (IOException e) { throw FSRecords.handleError(e); } } }
private ArchiveRoot( @NotNull NewVirtualFileSystem fs, int id, VfsData.Segment segment, VfsData.DirectoryData data, VirtualFile parentLocalFile) { super(id, segment, data, fs); myParentLocalFile = parentLocalFile; myParentPath = myParentLocalFile.getPath(); }
/** * Gets the common ancestor for passed files, or null if the files do not have common ancestors. * * @param file1 fist file * @param file2 second file * @return common ancestor for the passed files. Returns <code>null</code> if the files do not * have common ancestor */ @Nullable public static VirtualFile getCommonAncestor( @NotNull VirtualFile file1, @NotNull VirtualFile file2) { if (!file1.getFileSystem().equals(file2.getFileSystem())) { return null; } VirtualFile[] path1 = getPathComponents(file1); VirtualFile[] path2 = getPathComponents(file2); int lastEqualIdx = -1; for (int i = 0; i < path1.length && i < path2.length; i++) { if (path1[i].equals(path2[i])) { lastEqualIdx = i; } else { break; } } return lastEqualIdx == -1 ? null : path1[lastEqualIdx]; }
private void executeDelete(@NotNull VirtualFile file) { if (!file.exists()) { LOG.error("Deleting a file, which does not exist: " + file.getPath()); return; } clearIdCache(); int id = getFileId(file); final VirtualFile parent = file.getParent(); final int parentId = parent == null ? 0 : getFileId(parent); if (parentId == 0) { String rootUrl = normalizeRootUrl(file.getPath(), (NewVirtualFileSystem) file.getFileSystem()); myRootsLock.writeLock().lock(); try { myRoots.remove(rootUrl); myRootsById.remove(id); FSRecords.deleteRootRecord(id); } finally { myRootsLock.writeLock().unlock(); } } else { removeIdFromParentList(parentId, id, parent, file); VirtualDirectoryImpl directory = (VirtualDirectoryImpl) file.getParent(); assert directory != null : file; directory.removeChild(file); } FSRecords.deleteRecordRecursively(id); invalidateSubtree(file); }
public static VirtualFile copyFileRelative( Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile toDir, @NotNull String relativePath) throws IOException { StringTokenizer tokenizer = new StringTokenizer(relativePath, "/"); VirtualFile curDir = toDir; while (true) { String token = tokenizer.nextToken(); if (tokenizer.hasMoreTokens()) { VirtualFile childDir = curDir.findChild(token); if (childDir == null) { childDir = curDir.createChildDirectory(requestor, token); } curDir = childDir; } else { return copyFile(requestor, file, curDir, token); } } }
/** * Copies content of resource to the given file * * @param file to copy to * @param resourceUrl url of the resource to be copied * @throws java.io.IOException if resource not found or copying failed */ public static void copyFromResource( @NotNull VirtualFile file, @NonNls @NotNull String resourceUrl) throws IOException { InputStream out = VfsUtil.class.getResourceAsStream(resourceUrl); if (out == null) { throw new FileNotFoundException(resourceUrl); } try { byte[] bytes = FileUtil.adaptiveLoadBytes(out); file.setBinaryContent(bytes); } finally { out.close(); } }
public static void processFilesRecursively( @NotNull VirtualFile root, @NotNull Processor<VirtualFile> processor, @NotNull Convertor<VirtualFile, Boolean> directoryFilter) { if (!processor.process(root)) return; if (root.isDirectory() && directoryFilter.convert(root)) { final LinkedList<VirtualFile[]> queue = new LinkedList<VirtualFile[]>(); queue.add(root.getChildren()); do { final VirtualFile[] files = queue.removeFirst(); for (VirtualFile file : files) { if (!processor.process(file)) return; if (file.isDirectory() && directoryFilter.convert(file)) { queue.add(file.getChildren()); } } } while (!queue.isEmpty()); } }
/** * Gets an array of files representing paths from root to the passed file. * * @param file the file * @return virtual files which represents paths from root to the passed file */ @NotNull private static VirtualFile[] getPathComponents(@NotNull VirtualFile file) { ArrayList<VirtualFile> componentsList = new ArrayList<VirtualFile>(); while (file != null) { componentsList.add(file); file = file.getParent(); } int size = componentsList.size(); VirtualFile[] components = new VirtualFile[size]; for (int i = 0; i < size; i++) { components[i] = componentsList.get(size - i - 1); } return components; }
public static boolean processFilesRecursively( @NotNull VirtualFile root, @NotNull Processor<VirtualFile> processor) { if (!processor.process(root)) return false; if (root.isDirectory()) { final LinkedList<VirtualFile[]> queue = new LinkedList<VirtualFile[]>(); queue.add(root.getChildren()); do { final VirtualFile[] files = queue.removeFirst(); for (VirtualFile file : files) { if (!processor.process(file)) return false; if (file.isDirectory()) { queue.add(file.getChildren()); } } } while (!queue.isEmpty()); } return true; }
public static void markDirtyAndRefresh( boolean async, boolean recursive, boolean loadChildren, VirtualFile... files) { List<VirtualFile> list = ContainerUtil.filter(Condition.NOT_NULL, files); if (list.isEmpty()) { return; } for (VirtualFile file : list) { if (loadChildren) { file.getChildren(); } if (file instanceof NewVirtualFile) { if (recursive) { ((NewVirtualFile) file).markDirtyRecursively(); } else { ((NewVirtualFile) file).markDirty(); } } } LocalFileSystem.getInstance().refreshFiles(list, async, recursive, null); }
@Override public VirtualFile copyFile( Object requestor, @NotNull VirtualFile file, @NotNull VirtualFile parent, @NotNull String name) throws IOException { getDelegate(file).copyFile(requestor, file, parent, name); processEvent(new VFileCopyEvent(requestor, file, parent, name)); final VirtualFile child = parent.findChild(name); if (child == null) { throw new IOException("Cannot create child"); } return child; }