/** Enable semantic highlighting. */
  private void enable() {
    initializeHighlightings();

    fPresenter = new SemanticHighlightingPresenter();
    fPresenter.install(fSourceViewer, fPresentationReconciler);

    if (fEditor != null) {
      fReconciler = new SemanticHighlightingReconciler();
      fReconciler.install(
          fEditor, fSourceViewer, fPresenter, fSemanticHighlightings, fHighlightings);
    } else {
      fPresenter.updatePresentation(null, createHardcodedPositions(), new HighlightedPosition[0]);
    }
  }
  /**
   * Update the presentation.
   *
   * @param textPresentation the text presentation
   * @param addedPositions the added positions
   * @param removedPositions the removed positions
   */
  private void updatePresentation(
      TextPresentation textPresentation,
      List<Position> addedPositions,
      List<Position> removedPositions) {
    Runnable runnable =
        fJobPresenter.createUpdateRunnable(textPresentation, addedPositions, removedPositions);
    if (runnable == null) {
      return;
    }

    DartEditor editor = fEditor;
    if (editor == null) {
      return;
    }

    IWorkbenchPartSite site = editor.getSite();
    if (site == null) {
      return;
    }

    Shell shell = site.getShell();
    if (shell == null || shell.isDisposed()) {
      return;
    }

    Display display = shell.getDisplay();
    if (display == null || display.isDisposed()) {
      return;
    }

    display.asyncExec(runnable);
  }
  @Override
  public void reconciled(DartUnit ast, boolean forced, IProgressMonitor progressMonitor) {
    // don't update semantic highlighting if there are parsing problems to avoid "flashing"
    if (ast != null && ast.hasParseErrors()) {
      return;
    }

    // ensure at most one thread can be reconciling at any time
    synchronized (fReconcileLock) {
      if (fIsReconciling) {
        return;
      } else {
        fIsReconciling = true;
      }
    }
    fJobPresenter = fPresenter;
    fJobSemanticHighlightings = fSemanticHighlightings;
    fJobHighlightings = fHighlightings;

    try {
      if (fJobPresenter == null || fJobSemanticHighlightings == null || fJobHighlightings == null) {
        return;
      }

      fJobPresenter.setCanceled(progressMonitor.isCanceled());

      if (ast == null || ast.getLibrary() == null || fJobPresenter.isCanceled()) {
        return;
      }

      DartNode[] subtrees = getAffectedSubtrees(ast);
      if (subtrees.length == 0) {
        return;
      }

      startReconcilingPositions();

      if (!fJobPresenter.isCanceled()) {
        reconcilePositions(subtrees);
      }

      TextPresentation textPresentation = null;
      if (!fJobPresenter.isCanceled()) {
        textPresentation = fJobPresenter.createPresentation(fAddedPositions, fRemovedPositions);
      }

      if (!fJobPresenter.isCanceled()) {
        updatePresentation(textPresentation, fAddedPositions, fRemovedPositions);
      }

      stopReconcilingPositions();
    } finally {
      fJobPresenter = null;
      fJobSemanticHighlightings = null;
      fJobHighlightings = null;
      synchronized (fReconcileLock) {
        fIsReconciling = false;
      }
    }
  }
  /** Disable semantic highlighting. */
  private void disable() {
    if (fReconciler != null) {
      fReconciler.uninstall();
      fReconciler = null;
    }

    if (fPresenter != null) {
      fPresenter.uninstall();
      fPresenter = null;
    }

    if (fSemanticHighlightings != null) disposeHighlightings();
  }
  /** Uninstall this reconciler from the editor */
  public void uninstall() {
    if (fPresenter != null) fPresenter.setCanceled(true);

    if (fEditor != null) {
      if (fEditor instanceof CompilationUnitEditor)
        ((CompilationUnitEditor) fEditor).removeReconcileListener(this);
      else fSourceViewer.removeTextInputListener(this);
      fEditor = null;
    }

    fSourceViewer = null;
    fSemanticHighlightings = null;
    fHighlightings = null;
    fPresenter = null;
  }
  /*
   * @see edu.berkeley.eduride.isa.ui.text.java.IJavaReconcilingListener#reconciled(CompilationUnit, boolean, IProgressMonitor)
   */
  public void reconciled(CompilationUnit ast, boolean forced, IProgressMonitor progressMonitor) {
    // ensure at most one thread can be reconciling at any time
    synchronized (fReconcileLock) {
      if (fIsReconciling) return;
      else fIsReconciling = true;
    }
    fJobPresenter = fPresenter;
    fJobSemanticHighlightings = fSemanticHighlightings;
    fJobHighlightings = fHighlightings;

    try {
      if (fJobPresenter == null || fJobSemanticHighlightings == null || fJobHighlightings == null)
        return;

      fJobPresenter.setCanceled(progressMonitor.isCanceled());

      if (ast == null || fJobPresenter.isCanceled()) return;

      ASTNode[] subtrees = getAffectedSubtrees(ast);
      if (subtrees.length == 0) return;

      startReconcilingPositions();

      if (!fJobPresenter.isCanceled()) {
        fJobDeprecatedMemberHighlighting = null;
        for (int i = 0, n = fJobSemanticHighlightings.length; i < n; i++) {
          SemanticHighlighting semanticHighlighting = fJobSemanticHighlightings[i];
          if (fJobHighlightings[i].isEnabled()
              && semanticHighlighting instanceof DeprecatedMemberHighlighting) {
            fJobDeprecatedMemberHighlighting = fJobHighlightings[i];
            break;
          }
        }
        reconcilePositions(subtrees);
      }

      TextPresentation textPresentation = null;
      if (!fJobPresenter.isCanceled())
        textPresentation = fJobPresenter.createPresentation(fAddedPositions, fRemovedPositions);

      if (!fJobPresenter.isCanceled())
        updatePresentation(textPresentation, fAddedPositions, fRemovedPositions);

      stopReconcilingPositions();
    } finally {
      fJobPresenter = null;
      fJobSemanticHighlightings = null;
      fJobHighlightings = null;
      fJobDeprecatedMemberHighlighting = null;
      synchronized (fReconcileLock) {
        fIsReconciling = false;
      }
    }
  }
  /**
   * Computes the hard-coded positions from the hard-coded ranges
   *
   * @return the hard-coded positions
   */
  private HighlightedPosition[] createHardcodedPositions() {
    List<HighlightedPosition> positions = new ArrayList<HighlightedPosition>();
    for (int i = 0; i < fHardcodedRanges.length; i++) {
      HighlightedRange range = null;
      Highlighting hl = null;
      for (int j = 0; j < fHardcodedRanges[i].length; j++) {
        hl = getHighlighting(fHardcodedRanges[i][j].getKey());
        if (hl.isEnabled()) {
          range = fHardcodedRanges[i][j];
          break;
        }
      }

      if (range != null)
        positions.add(
            fPresenter.createHighlightedPosition(range.getOffset(), range.getLength(), hl));
    }
    return positions.toArray(new HighlightedPosition[positions.size()]);
  }
 /** Start reconciling positions. */
 private void startReconcilingPositions() {
   fJobPresenter.addAllPositions(fRemovedPositions);
   fNOfRemovedPositions = fRemovedPositions.size();
 }
  /**
   * Handle the given property change event
   *
   * @param event The event
   */
  private void handlePropertyChangeEvent(PropertyChangeEvent event) {
    if (fPreferenceStore == null) return; // Uninstalled during event notification

    if (fConfiguration != null) fConfiguration.handlePropertyChangeEvent(event);

    if (SemanticHighlightings.affectsEnablement(fPreferenceStore, event)) {
      if (isEnabled()) enable();
      else disable();
    }

    if (!isEnabled()) return;

    boolean refreshNeeded = false;

    for (int i = 0, n = fSemanticHighlightings.length; i < n; i++) {
      SemanticHighlighting semanticHighlighting = fSemanticHighlightings[i];

      String colorKey = SemanticHighlightings.getColorPreferenceKey(semanticHighlighting);
      if (colorKey.equals(event.getProperty())) {
        adaptToTextForegroundChange(fHighlightings[i], event);
        fPresenter.highlightingStyleChanged(fHighlightings[i]);
        refreshNeeded = true;
        continue;
      }

      String boldKey = SemanticHighlightings.getBoldPreferenceKey(semanticHighlighting);
      if (boldKey.equals(event.getProperty())) {
        adaptToTextStyleChange(fHighlightings[i], event, SWT.BOLD);
        fPresenter.highlightingStyleChanged(fHighlightings[i]);
        refreshNeeded = true;
        continue;
      }

      String italicKey = SemanticHighlightings.getItalicPreferenceKey(semanticHighlighting);
      if (italicKey.equals(event.getProperty())) {
        adaptToTextStyleChange(fHighlightings[i], event, SWT.ITALIC);
        fPresenter.highlightingStyleChanged(fHighlightings[i]);
        refreshNeeded = true;
        continue;
      }

      String strikethroughKey =
          SemanticHighlightings.getStrikethroughPreferenceKey(semanticHighlighting);
      if (strikethroughKey.equals(event.getProperty())) {
        adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.STRIKETHROUGH);
        fPresenter.highlightingStyleChanged(fHighlightings[i]);
        refreshNeeded = true;
        continue;
      }

      String underlineKey = SemanticHighlightings.getUnderlinePreferenceKey(semanticHighlighting);
      if (underlineKey.equals(event.getProperty())) {
        adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.UNDERLINE);
        fPresenter.highlightingStyleChanged(fHighlightings[i]);
        refreshNeeded = true;
        continue;
      }

      String enabledKey = SemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting);
      if (enabledKey.equals(event.getProperty())) {
        adaptToEnablementChange(fHighlightings[i], event);
        fPresenter.highlightingStyleChanged(fHighlightings[i]);
        refreshNeeded = true;
        continue;
      }
    }

    if (refreshNeeded && fReconciler != null) fReconciler.refresh();
  }