/**
  * 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;
  }
Exemple #13
0
  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();
 }
Exemple #17
0
  @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));
 }
Exemple #20
0
  @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);
      }
    }
  }
Exemple #21
0
 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];
  }
Exemple #23
0
  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;
  }
Exemple #29
0
  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;
  }