/**
   * delete file or directory (both 'undo' and 'do' modes) unversioned: do nothing, return false
   * obstructed: do nothing, return false external or wc root: do nothing, return false missing: do
   * nothing, return false
   *
   * <p>versioned: schedule for deletion, return true added: schedule for deletion (make
   * unversioned), return true copied, but not scheduled: schedule for deletion, return true
   * replaced: schedule for deletion, return true
   *
   * <p>deleted: do nothing, return true (strange)
   */
  public boolean delete(VirtualFile file) throws IOException {
    final SvnVcs vcs = getVCS(file);
    if (vcs != null && SvnUtil.isAdminDirectory(file)) {
      return true;
    }
    if (vcs == null) return false;
    final VcsShowConfirmationOption.Value value = vcs.getDeleteConfirmation().getValue();
    if (VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY.equals(value)) return false;

    final File ioFile = getIOFile(file);
    if (!SvnUtil.isSvnVersioned(vcs.getProject(), ioFile.getParentFile())) {
      return false;
    }
    if (SvnUtil.isWorkingCopyRoot(ioFile)) {
      return false;
    }

    SVNStatus status = getFileStatus(vcs, ioFile);

    if (status == null
        || SvnVcs.svnStatusIsUnversioned(status)
        || SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_OBSTRUCTED)
        || SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_MISSING)
        || SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL)
        || SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) {
      return false;
    } else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
      if (isUndo(vcs)) {
        moveToUndoStorage(file);
      }
      return true;
    } else {
      if (vcs != null) {
        if (isAboveSourceOfCopyOrMove(vcs.getProject(), ioFile)) {
          myDeletedFiles.putValue(vcs.getProject(), ioFile);
          return true;
        }
        if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) {
          try {
            createRevertAction(vcs, ioFile, false).execute();
          } catch (SVNException e) {
            // ignore
          }
        } else {
          myDeletedFiles.putValue(vcs.getProject(), ioFile);
          // packages deleted from disk should not be deleted from svn (IDEADEV-16066)
          if (file.isDirectory() || isUndo(vcs)) return true;
        }
      }
      return false;
    }
  }
    public Pair<List<RepositoryLocationGroup>, List<RepositoryLocation>> groupLocations(
        final List<RepositoryLocation> in) {
      final List<RepositoryLocationGroup> groups = new ArrayList<RepositoryLocationGroup>();
      final List<RepositoryLocation> singles = new ArrayList<RepositoryLocation>();

      final MultiMap<SVNURL, RepositoryLocation> map = new MultiMap<SVNURL, RepositoryLocation>();

      for (RepositoryLocation location : in) {
        final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location;
        final String url = svnLocation.getURL();

        final SVNURL root = SvnUtil.getRepositoryRoot(myVcs, url);
        if (root == null) {
          // should not occur
          LOG.info("repository root not found for location:" + location.toPresentableString());
          singles.add(location);
        } else {
          map.putValue(root, svnLocation);
        }
      }

      final Set<SVNURL> keys = map.keySet();
      for (SVNURL key : keys) {
        final Collection<RepositoryLocation> repositoryLocations = map.get(key);
        if (repositoryLocations.size() == 1) {
          singles.add(repositoryLocations.iterator().next());
        } else {
          final SvnRepositoryLocationGroup group =
              new SvnRepositoryLocationGroup(key, repositoryLocations);
          groups.add(group);
        }
      }
      return new Pair<List<RepositoryLocationGroup>, List<RepositoryLocation>>(groups, singles);
    }
  /**
   * add file or directory:
   *
   * <p>parent directory is: unversioned: do nothing, return false versioned: entry is: null: create
   * entry, schedule for addition missing: do nothing, return false deleted, 'do' mode: try to
   * create entry and it schedule for addition if kind is the same, otherwise do nothing, return
   * false. deleted: 'undo' mode: try to revert non-recursively, if kind is the same, otherwise do
   * nothing, return false. anything else: return false.
   */
  private boolean createItem(
      VirtualFile dir, String name, boolean directory, final boolean recursive) {
    SvnVcs vcs = getVCS(dir);
    if (vcs == null) {
      return false;
    }
    final VcsShowConfirmationOption.Value value = vcs.getAddConfirmation().getValue();
    if (VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY.equals(value)) return false;

    if (isUndo(vcs) && SvnUtil.isAdminDirectory(dir, name)) {
      return false;
    }
    File ioDir = getIOFile(dir);
    boolean pendingAdd = isPendingAdd(vcs.getProject(), dir);
    if (!SvnUtil.isSvnVersioned(vcs.getProject(), ioDir) && !pendingAdd) {
      return false;
    }
    final File targetFile = new File(ioDir, name);
    SVNStatus status = getFileStatus(vcs, targetFile);

    if (status == null
        || status.getContentsStatus() == SVNStatusType.STATUS_NONE
        || status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED) {
      myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive));
      return false;
    } else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_MISSING)) {
      return false;
    } else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
      SVNNodeKind kind = status.getKind();
      // kind differs.
      if (directory && kind != SVNNodeKind.DIR || !directory && kind != SVNNodeKind.FILE) {
        return false;
      }
      try {
        if (isUndo(vcs)) {
          createRevertAction(vcs, targetFile, false).execute();
          return true;
        }
        myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive));
        return false;
      } catch (SVNException e) {
        SVNFileUtil.deleteAll(targetFile, true);
        return false;
      }
    }
    return false;
  }
  /**
   * Returns real working copies roots - if there is <Project Root> -> Subversion setting, and there
   * is one working copy, will return one root
   */
  public List<WCInfo> getAllWcInfos() {
    final SvnFileUrlMapping urlMapping = getSvnFileUrlMapping();

    final List<RootUrlInfo> infoList = urlMapping.getAllWcInfos();
    final List<WCInfo> infos = new ArrayList<WCInfo>();
    for (RootUrlInfo info : infoList) {
      final File file = info.getIoFile();
      infos.add(
          new WCInfo(
              file.getAbsolutePath(),
              info.getAbsoluteUrlAsUrl(),
              info.getFormat(),
              info.getRepositoryUrl(),
              SvnUtil.isWorkingCopyRoot(file),
              info.getType(),
              SvnUtil.getDepth(this, file)));
    }
    return infos;
  }
 public SVNURL ask(final SVNURL url) {
   for (SVNURL root : myRoots) {
     if (root.equals(SVNURLUtil.getCommonURLAncestor(root, url))) {
       return root;
     }
   }
   final SVNURL newUrl = SvnUtil.getRepositoryRoot(myVcs, url);
   if (newUrl != null) {
     myRoots.add(newUrl);
     return newUrl;
   }
   return null;
 }
  @Nullable
  public File copy(final VirtualFile file, final VirtualFile toDir, final String copyName)
      throws IOException {
    SvnVcs vcs = getVCS(toDir);
    if (vcs == null) {
      vcs = getVCS(file);
    }
    if (vcs == null) {
      return null;
    }

    File srcFile = new File(file.getPath());
    File destFile = new File(new File(toDir.getPath()), copyName);
    final boolean dstDirUnderControl =
        SvnUtil.isSvnVersioned(vcs.getProject(), destFile.getParentFile());
    if (!dstDirUnderControl && !isPendingAdd(vcs.getProject(), toDir)) {
      return null;
    }

    if (!SvnUtil.isSvnVersioned(vcs.getProject(), srcFile.getParentFile())) {
      myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false));
      return null;
    }

    final SVNStatus fileStatus = getFileStatus(vcs, srcFile);
    if (fileStatus != null && SvnVcs.svnStatusIs(fileStatus, SVNStatusType.STATUS_ADDED)) {
      myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false));
      return null;
    }

    if (sameRoot(vcs, file.getParent(), toDir)) {
      myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, srcFile, false));
      return null;
    }

    myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false));
    return null;
  }
  @Override
  public <S> List<S> filterUniqueRoots(
      final List<S> in, final Convertor<S, VirtualFile> convertor) {
    if (in.size() <= 1) return in;

    final List<MyPair<S>> infos = new ArrayList<MyPair<S>>(in.size());
    final SvnFileUrlMappingImpl mapping = (SvnFileUrlMappingImpl) getSvnFileUrlMapping();
    final List<S> notMatched = new LinkedList<S>();
    for (S s : in) {
      final VirtualFile vf = convertor.convert(s);
      if (vf == null) continue;

      final File ioFile = new File(vf.getPath());
      SVNURL url = mapping.getUrlForFile(ioFile);
      if (url == null) {
        url = SvnUtil.getUrl(this, ioFile);
        if (url == null) {
          notMatched.add(s);
          continue;
        }
      }
      infos.add(new MyPair<S>(vf, url.toString(), s));
    }
    final List<MyPair<S>> filtered = new ArrayList<MyPair<S>>(infos.size());
    ForNestedRootChecker.filterOutSuperfluousChildren(this, infos, filtered);

    final List<S> converted =
        ObjectsConvertor.convert(
            filtered,
            new Convertor<MyPair<S>, S>() {
              @Override
              public S convert(final MyPair<S> o) {
                return o.getSrc();
              }
            });
    if (!notMatched.isEmpty()) {
      // potential bug is here: order is not kept. but seems it only occurs for cases where result
      // is sorted after filtering so ok
      converted.addAll(notMatched);
    }
    return converted;
  }
 @Nullable
 public RepositoryLocation getLocationFor(final FilePath root) {
   final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
   final String url = SvnUtil.getExactLocation(myVcs, root.getIOFile());
   return url == null ? null : new SvnRepositoryLocation(url);
 }
 @Override
 public boolean isVersionedDirectory(final VirtualFile dir) {
   return SvnUtil.seemsLikeVersionedDir(dir);
 }