@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));
 }
 /**
  * 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);
   }
 }
  @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;
  }
 /**
  * Copies all files matching the <code>filter</code> from <code>fromDir</code> to <code>toDir
  * </code>.
  *
  * @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 fromDir the directory to copy from
  * @param toDir the directory to copy to
  * @param filter {@link VirtualFileFilter}
  * @throws IOException if files failed to be copied
  */
 public static void copyDirectory(
     Object requestor,
     @NotNull VirtualFile fromDir,
     @NotNull VirtualFile toDir,
     @Nullable VirtualFileFilter filter)
     throws IOException {
   VirtualFile[] children = fromDir.getChildren();
   for (VirtualFile child : children) {
     if (filter == null || filter.accept(child)) {
       if (!child.isDirectory()) {
         copyFile(requestor, child, toDir);
       } else {
         VirtualFile newChild = toDir.findChild(child.getName());
         if (newChild == null) {
           newChild = toDir.createChildDirectory(requestor, child.getName());
         }
         copyDirectory(requestor, child, newChild, filter);
       }
     }
   }
 }
 /**
  * Copies all files matching the <code>filter</code> from <code>fromDir</code> to <code>toDir
  * </code>. Symlinks end special files are ignored.
  *
  * @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 fromDir the directory to copy from
  * @param toDir the directory to copy to
  * @param filter {@link VirtualFileFilter}
  * @throws IOException if files failed to be copied
  */
 public static void copyDirectory(
     Object requestor,
     @NotNull VirtualFile fromDir,
     @NotNull VirtualFile toDir,
     @Nullable VirtualFileFilter filter)
     throws IOException {
   @SuppressWarnings("UnsafeVfsRecursion")
   VirtualFile[] children = fromDir.getChildren();
   for (VirtualFile child : children) {
     if (!child.isSymLink()
         && !child.isSpecialFile()
         && (filter == null || filter.accept(child))) {
       if (!child.isDirectory()) {
         copyFile(requestor, child, toDir);
       } else {
         VirtualFile newChild = toDir.findChild(child.getName());
         if (newChild == null) {
           newChild = toDir.createChildDirectory(requestor, child.getName());
         }
         copyDirectory(requestor, child, newChild, filter);
       }
     }
   }
 }
  private static boolean writeAttributesToRecord(
      final int id,
      final int parentId,
      @NotNull VirtualFile file,
      @NotNull NewVirtualFileSystem fs,
      @NotNull FileAttributes attributes) {
    String name = file.getName();
    if (!name.isEmpty()) {
      if (namesEqual(fs, name, FSRecords.getName(id)))
        return false; // TODO: Handle root attributes change.
    } else {
      if (areChildrenLoaded(id)) return false; // TODO: hack
    }

    FSRecords.writeAttributesToRecord(id, parentId, attributes, name);

    return true;
  }
  @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;
  }
 @NotNull
 @Override
 public CharSequence getNameSequence() {
   return myParentLocalFile.getName();
 }
 @NotNull
 @Override
 public String getName() {
   return myParentLocalFile.getName();
 }