/** Pair method to {@link #annotate}. It releases all resources. */
  private void release() {
    editorUI.removePropertyChangeListener(this);
    textComponent.removeComponentListener(this);
    doc.removeDocumentListener(this);
    caret.removeChangeListener(this);
    if (caretTimer != null) {
      caretTimer.removeActionListener(this);
    }
    elementAnnotations = Collections.<Element, AnnotateLine>emptyMap();
    previousRevisions = null;
    originalFiles = null;
    // cancel running annotation task if active
    if (latestAnnotationTask != null) {
      latestAnnotationTask.cancel();
    }
    AnnotationMarkProvider amp = AnnotationMarkInstaller.getMarkProvider(textComponent);
    if (amp != null) {
      amp.setMarks(Collections.<AnnotationMark>emptyList());
    }

    clearRecentFeedback();
  }
  // latestAnnotationTask business logic
  @Override
  public void run() {
    // get resource bundle
    ResourceBundle loc = NbBundle.getBundle(AnnotationBar.class);
    // give status bar "wait" indication // NOI18N
    StatusBar statusBar = editorUI.getStatusBar();
    recentStatusMessage = loc.getString("CTL_StatusBar_WaitFetchAnnotation"); // NOI18N
    statusBar.setText(StatusBar.CELL_MAIN, recentStatusMessage);

    // determine current line
    int line = -1;
    int offset = caret.getDot();
    try {
      line = Utilities.getLineOffset(doc, offset);
    } catch (BadLocationException ex) {
      Mercurial.LOG.log(Level.SEVERE, "Can not get line for caret at offset ", offset); // NOI18N
      clearRecentFeedback();
      return;
    }

    // handle locally modified lines
    AnnotateLine al = getAnnotateLine(line);
    if (al == null) {
      AnnotationMarkProvider amp = AnnotationMarkInstaller.getMarkProvider(textComponent);
      if (amp != null) {
        amp.setMarks(Collections.<AnnotationMark>emptyList());
      }
      clearRecentFeedback();
      if (recentRevision != null) {
        recentRevision = null;
        repaint();
      }
      return;
    }

    // handle unchanged lines
    String revision = al.getRevision();
    if (revision.equals(recentRevision) == false) {
      recentRevision = revision;
      repositoryRoot = Mercurial.getInstance().getRepositoryRoot(getCurrentFile());
      repaint();

      AnnotationMarkProvider amp = AnnotationMarkInstaller.getMarkProvider(textComponent);
      if (amp != null) {

        List<AnnotationMark> marks = new ArrayList<AnnotationMark>(elementAnnotations.size());
        // I cannot affort to lock elementAnnotations for long time
        // it's accessed from editor thread too
        Iterator<Map.Entry<Element, AnnotateLine>> it2;
        synchronized (elementAnnotations) {
          it2 =
              new HashSet<Map.Entry<Element, AnnotateLine>>(elementAnnotations.entrySet())
                  .iterator();
        }
        while (it2.hasNext()) {
          Map.Entry<Element, AnnotateLine> next = it2.next();
          AnnotateLine annotateLine = next.getValue();
          if (revision.equals(annotateLine.getRevision())) {
            Element element = next.getKey();
            if (elementAnnotations.containsKey(element) == false) {
              continue;
            }
            int elementOffset = element.getStartOffset();
            int lineNumber = NbDocument.findLineNumber((StyledDocument) doc, elementOffset);
            AnnotationMark mark = new AnnotationMark(lineNumber, revision);
            marks.add(mark);
          }

          if (Thread.interrupted()) {
            clearRecentFeedback();
            return;
          }
        }
        amp.setMarks(marks);
      }
    }

    if (al.getCommitMessage() != null) {
      recentStatusMessage = al.getCommitMessage();
      statusBar.setText(
          StatusBar.CELL_MAIN,
          al.getRevision()
              + ":"
              + al.getId()
              + " - "
              + al.getAuthor()
              + ": "
              + recentStatusMessage); // NOI18N
    } else {
      clearRecentFeedback();
    }
  }