private static void invalidateSubtree(@NotNull VirtualFile file) { final VirtualFileSystemEntry impl = (VirtualFileSystemEntry) file; impl.invalidate(); for (VirtualFile child : impl.getCachedChildren()) { invalidateSubtree(child); } }
@Nullable private VirtualFileSystemEntry findChild( @NotNull String name, boolean doRefresh, boolean ensureCanonicalName, @NotNull NewVirtualFileSystem delegate) { boolean ignoreCase = !delegate.isCaseSensitive(); Comparator comparator = getComparator(ignoreCase); VirtualFileSystemEntry result = doFindChild(name, ensureCanonicalName, delegate, comparator); if (result == NULL_VIRTUAL_FILE) { result = doRefresh ? createAndFindChildWithEventFire(name, delegate) : null; } else if (result != null && doRefresh && delegate.isDirectory(result) != result.isDirectory()) { RefreshQueue.getInstance().refresh(false, false, null, result); result = findChild(name, false, ensureCanonicalName, delegate); } if (result == null) { addToAdoptedChildren(!delegate.isCaseSensitive(), name, comparator); } return result; }
// optimisation: do not travel up unnecessary private void markDirtyRecursivelyInternal() { for (VirtualFileSystemEntry child : getArraySafely()) { if (isAdoptedChild(child)) break; child.markDirtyInternal(); if (child instanceof VirtualDirectoryImpl) { ((VirtualDirectoryImpl) child).markDirtyRecursivelyInternal(); } } }
@Nullable private VirtualFileSystemEntry findFileById( int id, boolean cachedOnly, TIntArrayList visited, int mask) { VirtualFileSystemEntry cached = myIdToDirCache.get(id); if (cached != null) return cached; if (visited != null && (visited.size() >= DEPTH_LIMIT || (mask & id) == id && visited.contains(id))) { @NonNls String sb = "Dead loop detected in persistent FS (id=" + id + " cached-only=" + cachedOnly + "):"; for (int i = 0; i < visited.size(); i++) { int _id = visited.get(i); sb += "\n " + _id + " '" + getName(_id) + "' " + String.format("%02x", getFileAttributes(_id)) + ' ' + myIdToDirCache.containsKey(_id); } LOG.error(sb); return null; } int parentId = getParent(id); if (parentId >= id) { if (visited == null) visited = new TIntArrayList(DEPTH_LIMIT); } if (visited != null) visited.add(id); VirtualFileSystemEntry result; if (parentId == 0) { myRootsLock.readLock().lock(); try { result = myRootsById.get(id); } finally { myRootsLock.readLock().unlock(); } } else { VirtualFileSystemEntry parentFile = findFileById(parentId, cachedOnly, visited, mask | id); if (parentFile instanceof VirtualDirectoryImpl) { result = ((VirtualDirectoryImpl) parentFile).findChildById(id, cachedOnly); } else { result = null; } } if (result != null && result.isDirectory()) { VirtualFileSystemEntry old = myIdToDirCache.put(id, result); if (old != null) result = old; } return result; }
public VirtualFileSystemEntry findChildById(int id, boolean cachedOnly) { VirtualFile[] array = getArraySafely(); VirtualFileSystemEntry result = null; for (VirtualFile file : array) { VirtualFileSystemEntry withId = (VirtualFileSystemEntry) file; if (withId.getId() == id) { result = withId; break; } } if (result != null) return result; if (cachedOnly) return null; String name = ourPersistence.getName(id); return findChild(name, false, false, getFileSystem()); }
@TestOnly private static void assertAccessInTests( @NotNull VirtualFileSystemEntry child, @NotNull NewVirtualFileSystem delegate) { final Application application = ApplicationManager.getApplication(); if (IS_UNDER_TEAMCITY && SHOULD_PERFORM_ACCESS_CHECK && application.isUnitTestMode() && application instanceof ApplicationImpl && ((ApplicationImpl) application).isComponentsCreated()) { if (delegate != LocalFileSystem.getInstance() && delegate != JarFileSystem.getInstance()) { return; } // root' children are loaded always if (child.getParent() == null || child.getParent().getParent() == null) return; Set<String> allowed = allowedRoots(); boolean isUnder = allowed == null; if (!isUnder) { String childPath = child.getPath(); if (delegate == JarFileSystem.getInstance()) { VirtualFile local = JarFileSystem.getInstance().getVirtualFileForJar(child); assert local != null : child; childPath = local.getPath(); } for (String root : allowed) { if (FileUtil.startsWith(childPath, root)) { isUnder = true; break; } if (root.startsWith(JarFileSystem.PROTOCOL_PREFIX)) { String rootLocalPath = FileUtil.toSystemIndependentName(PathUtil.toPresentableUrl(root)); isUnder = FileUtil.startsWith(childPath, rootLocalPath); if (isUnder) break; } } } assert isUnder || allowed.isEmpty() : "File accessed outside allowed roots: " + child + ";\nAllowed roots: " + new ArrayList<String>(allowed); } }
@NotNull public VirtualFileSystemEntry createChild( @NotNull String name, int id, @NotNull NewVirtualFileSystem delegate) { VirtualFileSystemEntry child; final int attributes = ourPersistence.getFileAttributes(id); if (PersistentFS.isDirectory(attributes)) { child = new VirtualDirectoryImpl(name, this, delegate, id, attributes); } else { child = new VirtualFileImpl(name, this, id, attributes); //noinspection TestOnlyProblems assertAccessInTests(child, delegate); } if (delegate.markNewFilesAsDirty()) { child.markDirty(); } return child; }
@Override protected char[] appendPathOnFileSystem(int accumulatedPathLength, int[] positionRef) { char[] chars = getParent() .appendPathOnFileSystem(accumulatedPathLength + 1 + myName.length(), positionRef); if (positionRef[0] > 0 && chars[positionRef[0] - 1] != '/') { chars[positionRef[0]++] = '/'; } positionRef[0] = VirtualFileSystemEntry.copyString(chars, positionRef[0], myName); return chars; }
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 void executeTouch( @NotNull VirtualFile file, boolean reloadContentFromDelegate, long newModificationStamp) { if (reloadContentFromDelegate) { setFlag(file, MUST_RELOAD_CONTENT, true); } final NewVirtualFileSystem delegate = getDelegate(file); final FileAttributes attributes = delegate.getAttributes(file); FSRecords.setLength(getFileId(file), attributes != null ? attributes.length : DEFAULT_LENGTH); FSRecords.setTimestamp( getFileId(file), attributes != null ? attributes.lastModified : DEFAULT_TIMESTAMP); ((VirtualFileSystemEntry) file).setModificationStamp(newModificationStamp); }
private void assertConsistency( @NotNull VirtualFileSystemEntry[] array, boolean ignoreCase, @NotNull Object... details) { if (!CHECK) return; boolean allChildrenLoaded = allChildrenLoaded(); for (int i = 0; i < array.length; i++) { VirtualFileSystemEntry file = array[i]; boolean isAdopted = isAdoptedChild(file); assert !isAdopted || !allChildrenLoaded; if (isAdopted && i != array.length - 1) { assert isAdoptedChild(array[i + 1]); } if (i != 0) { VirtualFileSystemEntry prev = array[i - 1]; String prevName = prev.getName(); int cmp = file.compareNameTo(prevName, ignoreCase); if (cmp == 0) { error( verboseToString.fun(prev) + " equals to " + verboseToString.fun(file), array, details); } if (isAdopted == isAdoptedChild(prev)) { if (cmp <= 0) { error( "Not sorted: " + verboseToString.fun(prev) + " is not less than " + verboseToString.fun(file), array, details); } } } } }
public synchronized void addChild(@NotNull VirtualFileSystemEntry child) { VirtualFileSystemEntry[] array = myChildren; final String childName = child.getName(); final boolean ignoreCase = !getFileSystem().isCaseSensitive(); long r = findIndexInBoth(array, getComparator(childName, ignoreCase)); int indexInReal = (int) (r >> 32); int indexInAdopted = (int) r; if (indexInAdopted >= 0) { // remove Adopted first removeFromArray(indexInAdopted); } if (indexInReal < 0) { insertChildAt(child, indexInReal); } // else already stored assertConsistency(myChildren, ignoreCase, child); }
@Override public String fun(VirtualFileSystemEntry file) { //noinspection HardCodedStringLiteral return file + " (name: '" + file.getName() + "', " + file.getClass() + ", parent: " + file.getParent() + "; id: " + file.getId() + "; FS: " + file.getFileSystem() + "; delegate.attrs: " + file.getFileSystem().getAttributes(file) + "; caseSensitive: " + file.getFileSystem().isCaseSensitive() + "; canonical: " + file.getFileSystem().getCanonicallyCasedName(file) + ") "; }
private static void executeSetTarget(@NotNull VirtualFile file, String target) { ((VirtualFileSystemEntry) file).setLinkTarget(target); }
private static boolean isAdoptedChild(@NotNull VirtualFileSystemEntry v) { return v.getParent() == NULL_VIRTUAL_FILE; }
@Override public int compareFileNameTo(@NotNull String myName, @NotNull VirtualFileSystemEntry file) { return -file.compareNameTo(myName, true); }
private void assertConsistency( @NotNull VirtualFileSystemEntry[] array, boolean ignoreCase, @NotNull Object... details) { if (!CHECK) return; boolean allChildrenLoaded = allChildrenLoaded(); for (int i = 0; i < array.length; i++) { VirtualFileSystemEntry file = array[i]; boolean isAdopted = isAdoptedChild(file); assert !isAdopted || !allChildrenLoaded; if (isAdopted && i != array.length - 1) { assert isAdoptedChild(array[i + 1]); } if (i != 0) { VirtualFileSystemEntry prev = array[i - 1]; String prevName = prev.getName(); int cmp = file.compareNameTo(prevName, ignoreCase); if (cmp == 0) { Function<VirtualFileSystemEntry, String> verboseToString = new Function<VirtualFileSystemEntry, String>() { @Override public String fun(VirtualFileSystemEntry entry) { return entry + " (name: '" + entry.getName() + "', " + entry.getClass() + ", parent:" + entry.getParent() + "; id:" + entry.getId() + "; FS:" + entry.getFileSystem() + "; delegate.attrs:" + entry.getFileSystem().getAttributes(entry) + "; caseSensitive:" + entry.getFileSystem().isCaseSensitive() + "; canonical:" + entry.getFileSystem().getCanonicallyCasedName(entry) + ") "; } }; String children = StringUtil.join(array, verboseToString, ","); throw new AssertionError( verboseToString.fun(prev) + " equals to " + verboseToString.fun(file) + "; children: " + children + "\nDetails: " + ContainerUtil.map( details, new Function<Object, Object>() { @Override public Object fun(Object o) { return o instanceof Object[] ? Arrays.toString((Object[]) o) : o; } })); } if (isAdopted == isAdoptedChild(prev)) { assert cmp > 0 : "Not sorted. " + Arrays.toString(details); } } } }
private static void executeRename(@NotNull VirtualFile file, @NotNull final String newName) { final int id = getFileId(file); FSRecords.setName(id, newName); ((VirtualFileSystemEntry) file).setNewName(newName); }
private static void executeSetWritable(@NotNull VirtualFile file, boolean writableFlag) { setFlag(file, IS_READ_ONLY, !writableFlag); ((VirtualFileSystemEntry) file).updateProperty(VirtualFile.PROP_WRITABLE, writableFlag); }
@Override @Nullable public VirtualFileSystemEntry findRoot( @NotNull String basePath, @NotNull NewVirtualFileSystem fs) { if (basePath.isEmpty()) { LOG.error("Invalid root, fs=" + fs); return null; } String rootUrl = normalizeRootUrl(basePath, fs); myRootsLock.readLock().lock(); try { VirtualFileSystemEntry root = myRoots.get(rootUrl); if (root != null) return root; } finally { myRootsLock.readLock().unlock(); } final VirtualFileSystemEntry newRoot; int rootId = FSRecords.findRootRecord(rootUrl); VfsData.Segment segment = VfsData.getSegment(rootId, true); VfsData.DirectoryData directoryData = new VfsData.DirectoryData(); if (fs instanceof ArchiveFileSystem) { String parentPath = basePath.substring(0, basePath.indexOf(ArchiveFileSystem.ARCHIVE_SEPARATOR)); VirtualFile parentFile = LocalFileSystem.getInstance().findFileByPath(parentPath); if (parentFile == null) return null; FileType type = FileTypeRegistry.getInstance().getFileTypeByFileName(parentFile.getName()); if (!(type instanceof ArchiveFileType)) return null; newRoot = new ArchiveRoot(fs, rootId, segment, directoryData, parentFile); } else { newRoot = new FsRoot(fs, rootId, segment, directoryData, basePath); } FileAttributes attributes = fs.getAttributes( new StubVirtualFile() { @NotNull @Override public String getPath() { return newRoot.getPath(); } @Nullable @Override public VirtualFile getParent() { return null; } }); if (attributes == null || !attributes.isDirectory()) { return null; } boolean mark = false; myRootsLock.writeLock().lock(); try { VirtualFileSystemEntry root = myRoots.get(rootUrl); if (root != null) return root; VfsData.initFile(rootId, segment, -1, directoryData); mark = writeAttributesToRecord(rootId, 0, newRoot, fs, attributes); myRoots.put(rootUrl, newRoot); myRootsById.put(rootId, newRoot); } finally { myRootsLock.writeLock().unlock(); } if (!mark && attributes.lastModified != FSRecords.getTimestamp(rootId)) { newRoot.markDirtyRecursively(); } LOG.assertTrue( rootId == newRoot.getId(), "root=" + newRoot + " expected=" + rootId + " actual=" + newRoot.getId()); return newRoot; }
private static void executeSetHidden(@NotNull VirtualFile file, boolean hiddenFlag) { setFlag(file, IS_HIDDEN, hiddenFlag); ((VirtualFileSystemEntry) file).updateProperty(VirtualFile.PROP_HIDDEN, hiddenFlag); }