@Nullable
  public ProgressEvent convert(final String line) {
    // TODO: Add direct processing of "Summary of conflicts" lines at the end of "svn update" output (if there are conflicts).
    // TODO: Now it works ok because parseNormalLine could not determine necessary statuses from that and further lines
    if (StringUtil.isEmptyOrSpaces(line)) return null;

    if (line.startsWith(MERGING) || line.startsWith(RECORDING_MERGE_INFO)) {
      return null;
    } else if (line.startsWith(UPDATING)) {
      myRootsUnderProcessing.push(parseForPath(line));
      return createEvent(myRootsUnderProcessing.peek(), EventAction.UPDATE_NONE);
    } else if (line.startsWith(RESTORED)) {
      return createEvent(parseForPath(line), EventAction.RESTORE);
    } else if (line.startsWith(SKIPPED)) {
      // called, for instance, when folder is not working copy
      return createEvent(parseForPath(line), -1, EventAction.SKIP, parseComment(line));
    } else if (line.startsWith(FETCHING_EXTERNAL)) {
      myRootsUnderProcessing.push(parseForPath(line));
      return createEvent(myRootsUnderProcessing.peek(), EventAction.UPDATE_EXTERNAL);
    }

    for (final Pattern pattern : ourCompletePatterns) {
      final long revision = matchAndGetRevision(pattern, line);
      if (revision != -1) {
        // checkout output does not have special line like "Updating '.'" on start - so stack could be empty and we should use myBase
        File currentRoot = myRootsUnderProcessing.size() > 0 ? myRootsUnderProcessing.pop() : myBase;
        return createEvent(currentRoot, revision, EventAction.UPDATE_COMPLETED, null);
      }
    }

    return parseNormalString(line);
  }
@Nullable
  public ProgressEvent convert(final String line)