Package com.mountainminds.eclemma.internal.ui.annotation

Source Code of com.mountainminds.eclemma.internal.ui.annotation.CoverageAnnotationModel

/*******************************************************************************
* Copyright (c) 2006, 2014 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*    Marc R. Hoffmann - initial API and implementation
*   
******************************************************************************/
package com.mountainminds.eclemma.internal.ui.annotation;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModelEvent;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IAnnotationModelListener;
import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.jacoco.core.analysis.ICounter;
import org.jacoco.core.analysis.ICoverageNode;
import org.jacoco.core.analysis.ILine;
import org.jacoco.core.analysis.ISourceNode;

import com.mountainminds.eclemma.core.CoverageTools;
import com.mountainminds.eclemma.core.analysis.IJavaCoverageListener;
import com.mountainminds.eclemma.internal.ui.EclEmmaUIPlugin;

/**
* IAnnotationModel implementation for efficient coverage highlighting.
*/
public final class CoverageAnnotationModel implements IAnnotationModel {

  /** Key used to piggyback our model to the editor's model. */
  private static final Object KEY = new Object();

  /** List of current CoverageAnnotation objects */
  private List<CoverageAnnotation> annotations = new ArrayList<CoverageAnnotation>(
      32);

  /** List of registered IAnnotationModelListener */
  private List<IAnnotationModelListener> annotationModelListeners = new ArrayList<IAnnotationModelListener>(
      2);

  private final ITextEditor editor;
  private final IDocument document;
  private int openConnections = 0;
  private boolean annotated = false;

  private IJavaCoverageListener coverageListener = new IJavaCoverageListener() {
    public void coverageChanged() {
      updateAnnotations(true);
    }
  };

  private IDocumentListener documentListener = new IDocumentListener() {
    public void documentChanged(DocumentEvent event) {
      updateAnnotations(false);
    }

    public void documentAboutToBeChanged(DocumentEvent event) {
    }
  };

  private CoverageAnnotationModel(ITextEditor editor, IDocument document) {
    this.editor = editor;
    this.document = document;
    updateAnnotations(true);
  }

  /**
   * Attaches a coverage annotation model for the given editor if the editor can
   * be annotated. Does nothing if the model is already attached.
   *
   * @param editor
   *          Editor to attach a annotation model to
   */
  public static void attach(ITextEditor editor) {
    IDocumentProvider provider = editor.getDocumentProvider();
    // there may be text editors without document providers (SF #1725100)
    if (provider == null)
      return;
    IAnnotationModel model = provider.getAnnotationModel(editor
        .getEditorInput());
    if (!(model instanceof IAnnotationModelExtension))
      return;
    IAnnotationModelExtension modelex = (IAnnotationModelExtension) model;

    IDocument document = provider.getDocument(editor.getEditorInput());

    CoverageAnnotationModel coveragemodel = (CoverageAnnotationModel) modelex
        .getAnnotationModel(KEY);
    if (coveragemodel == null) {
      coveragemodel = new CoverageAnnotationModel(editor, document);
      modelex.addAnnotationModel(KEY, coveragemodel);
    }
  }

  /**
   * Detaches the coverage annotation model from the given editor. If the editor
   * does not have a model attached, this method does nothing.
   *
   * @param editor
   *          Editor to detach the annotation model from
   */
  public static void detach(ITextEditor editor) {
    IDocumentProvider provider = editor.getDocumentProvider();
    // there may be text editors without document providers (SF #1725100)
    if (provider == null)
      return;
    IAnnotationModel model = provider.getAnnotationModel(editor
        .getEditorInput());
    if (!(model instanceof IAnnotationModelExtension))
      return;
    IAnnotationModelExtension modelex = (IAnnotationModelExtension) model;
    modelex.removeAnnotationModel(KEY);
  }

  private void updateAnnotations(boolean force) {
    final ISourceNode coverage = findSourceCoverageForEditor();
    if (coverage != null) {
      if (!annotated || force) {
        createAnnotations(coverage);
        annotated = true;
      }
    } else {
      if (annotated) {
        clear();
        annotated = false;
      }
    }
  }

  private ISourceNode findSourceCoverageForEditor() {
    if (editor.isDirty()) {
      return null;
    }
    final IEditorInput input = editor.getEditorInput();
    if (input == null) {
      return null;
    }
    final Object element = input.getAdapter(IJavaElement.class);
    if (!hasSource((IJavaElement) element)) {
      return null;
    }
    return findSourceCoverageForElement(element);
  }

  private boolean hasSource(IJavaElement element) {
    if (element instanceof ISourceReference) {
      try {
        return ((ISourceReference) element).getSourceRange() != null;
      } catch (JavaModelException ex) {
        // we ignore this, the resource seems to have problems
      }
    }
    return false;
  }

  private ISourceNode findSourceCoverageForElement(Object element) {
    // Do we have a coverage info for the editor input?
    ICoverageNode coverage = CoverageTools.getCoverageInfo(element);
    if (coverage instanceof ISourceNode) {
      return (ISourceNode) coverage;
    }
    return null;
  }

  private void clear() {
    AnnotationModelEvent event = new AnnotationModelEvent(this);
    clear(event);
    fireModelChanged(event);
  }

  private void clear(AnnotationModelEvent event) {
    for (final CoverageAnnotation ca : annotations) {
      event.annotationRemoved(ca, ca.getPosition());
    }
    annotations.clear();
  }

  private void createAnnotations(final ISourceNode linecoverage) {
    AnnotationModelEvent event = new AnnotationModelEvent(this);
    clear(event);
    final int firstline = linecoverage.getFirstLine();
    final int lastline = Math.min(linecoverage.getLastLine(),
        document.getNumberOfLines());
    try {
      for (int l = firstline; l <= lastline; l++) {
        final ILine line = linecoverage.getLine(l);
        if (line.getStatus() != ICounter.EMPTY) {
          final IRegion region = document.getLineInformation(l - 1);
          final CoverageAnnotation ca = new CoverageAnnotation(
              region.getOffset(), region.getLength(), line);
          annotations.add(ca);
          event.annotationAdded(ca);
        }
      }
    } catch (BadLocationException ex) {
      EclEmmaUIPlugin.log(ex);
    }
    fireModelChanged(event);
  }

  public void addAnnotationModelListener(IAnnotationModelListener listener) {
    if (!annotationModelListeners.contains(listener)) {
      annotationModelListeners.add(listener);
      fireModelChanged(new AnnotationModelEvent(this, true));
    }
  }

  public void removeAnnotationModelListener(IAnnotationModelListener listener) {
    annotationModelListeners.remove(listener);
  }

  private void fireModelChanged(AnnotationModelEvent event) {
    event.markSealed();
    if (!event.isEmpty()) {
      for (final IAnnotationModelListener l : annotationModelListeners) {
        if (l instanceof IAnnotationModelListenerExtension) {
          ((IAnnotationModelListenerExtension) l).modelChanged(event);
        } else {
          l.modelChanged(this);
        }
      }
    }
  }

  public void connect(IDocument document) {
    if (this.document != document) {
      throw new IllegalArgumentException("Can't connect to different document."); //$NON-NLS-1$
    }
    for (final CoverageAnnotation ca : annotations) {
      try {
        document.addPosition(ca.getPosition());
      } catch (BadLocationException ex) {
        EclEmmaUIPlugin.log(ex);
      }
    }
    if (openConnections++ == 0) {
      CoverageTools.addJavaCoverageListener(coverageListener);
      document.addDocumentListener(documentListener);
    }
  }

  public void disconnect(IDocument document) {
    if (this.document != document) {
      throw new IllegalArgumentException(
          "Can't disconnect from different document."); //$NON-NLS-1$
    }
    for (final CoverageAnnotation ca : annotations) {
      document.removePosition(ca.getPosition());
    }
    if (--openConnections == 0) {
      CoverageTools.removeJavaCoverageListener(coverageListener);
      document.removeDocumentListener(documentListener);
    }
  }

  /**
   * External modification is not supported.
   */
  public void addAnnotation(Annotation annotation, Position position) {
    throw new UnsupportedOperationException();
  }

  /**
   * External modification is not supported.
   */
  public void removeAnnotation(Annotation annotation) {
    throw new UnsupportedOperationException();
  }

  public Iterator<?> getAnnotationIterator() {
    return annotations.iterator();
  }

  public Position getPosition(Annotation annotation) {
    if (annotation instanceof CoverageAnnotation) {
      return ((CoverageAnnotation) annotation).getPosition();
    } else {
      return null;
    }
  }

}
TOP

Related Classes of com.mountainminds.eclemma.internal.ui.annotation.CoverageAnnotationModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.