KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mountainminds > eclemma > internal > ui > annotation > CoverageAnnotationModel


1 /*******************************************************************************
2  * Copyright (c) 2006 Mountainminds GmbH & Co. KG
3  * This software is provided under the terms of the Eclipse Public License v1.0
4  * See http://www.eclipse.org/legal/epl-v10.html.
5  *
6  * $Id: CoverageAnnotationModel.java 330 2007-05-25 10:41:59Z mtnminds $
7  ******************************************************************************/

8 package com.mountainminds.eclemma.internal.ui.annotation;
9
10 import java.util.ArrayList JavaDoc;
11 import java.util.Iterator JavaDoc;
12 import java.util.List JavaDoc;
13
14 import org.eclipse.core.resources.IResource;
15 import org.eclipse.core.runtime.IAdaptable;
16 import org.eclipse.jdt.core.IJavaElement;
17 import org.eclipse.jdt.core.ISourceReference;
18 import org.eclipse.jdt.core.JavaModelException;
19 import org.eclipse.jface.text.BadLocationException;
20 import org.eclipse.jface.text.DocumentEvent;
21 import org.eclipse.jface.text.IDocument;
22 import org.eclipse.jface.text.IDocumentListener;
23 import org.eclipse.jface.text.IRegion;
24 import org.eclipse.jface.text.Position;
25 import org.eclipse.jface.text.source.Annotation;
26 import org.eclipse.jface.text.source.AnnotationModelEvent;
27 import org.eclipse.jface.text.source.IAnnotationModel;
28 import org.eclipse.jface.text.source.IAnnotationModelExtension;
29 import org.eclipse.jface.text.source.IAnnotationModelListener;
30 import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
31 import org.eclipse.ui.IEditorInput;
32 import org.eclipse.ui.texteditor.IDocumentProvider;
33 import org.eclipse.ui.texteditor.ITextEditor;
34
35 import com.mountainminds.eclemma.core.CoverageTools;
36 import com.mountainminds.eclemma.core.analysis.IJavaCoverageListener;
37 import com.mountainminds.eclemma.core.analysis.IJavaElementCoverage;
38 import com.mountainminds.eclemma.core.analysis.ILineCoverage;
39 import com.mountainminds.eclemma.internal.ui.EclEmmaUIPlugin;
40
41 /**
42  * IAnnotationModel implementation for efficient coverage highlighting.
43  *
44  * @author Marc R. Hoffmann
45  * @version $Revision: 330 $
46  */

47 public class CoverageAnnotationModel implements IAnnotationModel {
48   
49   /** Key used to piggyback our model to the editor's model. */
50   private static final Object JavaDoc KEY = new Object JavaDoc();
51   
52   /** List of current CoverageAnnotation objects */
53   private List JavaDoc annotations = new ArrayList JavaDoc(32);
54
55   /** List of registered IAnnotationModelListener */
56   private List JavaDoc annotationModelListeners = new ArrayList JavaDoc(2);
57   
58   private final ITextEditor editor;
59   private final IDocument document;
60   private int openConnections = 0;
61   private boolean annotated = false;
62   
63   private IJavaCoverageListener coverageListener = new IJavaCoverageListener() {
64     public void coverageChanged() {
65       updateAnnotations(true);
66     }
67   };
68   
69   private IDocumentListener documentListener = new IDocumentListener() {
70     public void documentChanged(DocumentEvent event) {
71       updateAnnotations(false);
72     }
73     public void documentAboutToBeChanged(DocumentEvent event) {
74     }
75   };
76   
77   private CoverageAnnotationModel(ITextEditor editor, IDocument document) {
78     this.editor = editor;
79     this.document = document;
80     updateAnnotations(true);
81   }
82   
83   /**
84    * Attaches a coverage annotation model for the given editor if the editor
85    * can be annotated. Does nothing if the model is already attached.
86    *
87    * @param editor Editor to attach a annotation model to
88    */

89   public static void attach(ITextEditor editor) {
90     IDocumentProvider provider = editor.getDocumentProvider();
91     // there may be text editors without document providers (SF #1725100)
92
if (provider == null) return;
93     IAnnotationModel model = provider.getAnnotationModel(editor.getEditorInput());
94     if (!(model instanceof IAnnotationModelExtension)) return;
95     IAnnotationModelExtension modelex = (IAnnotationModelExtension) model;
96     
97     IDocument document = provider.getDocument(editor.getEditorInput());
98     
99     CoverageAnnotationModel coveragemodel = (CoverageAnnotationModel) modelex.getAnnotationModel(KEY);
100     if (coveragemodel == null) {
101       coveragemodel = new CoverageAnnotationModel(editor, document);
102       modelex.addAnnotationModel(KEY, coveragemodel);
103     }
104   }
105   
106   /**
107    * Detaches the coverage annotation model from the given editor. If the editor
108    * does not have a model attached, this method does nothing.
109    *
110    * @param editor Editor to detach the annotation model from
111    */

112   public static void detach(ITextEditor editor) {
113     IDocumentProvider provider = editor.getDocumentProvider();
114     // there may be text editors without document providers (SF #1725100)
115
if (provider == null) return;
116     IAnnotationModel model = provider.getAnnotationModel(editor.getEditorInput());
117     if (!(model instanceof IAnnotationModelExtension)) return;
118     IAnnotationModelExtension modelex = (IAnnotationModelExtension) model;
119     modelex.removeAnnotationModel(KEY);
120   }
121   
122   protected void updateAnnotations(boolean force) {
123     ILineCoverage lineCoverage = null;
124     boolean annotate = false;
125     preconditions: {
126       if (editor.isDirty()) break preconditions;
127       IEditorInput input = editor.getEditorInput();
128       if (input == null) break preconditions;
129       Object JavaDoc element = input.getAdapter(IJavaElement.class);
130       lineCoverage = findLineCoverage(element);
131       if (lineCoverage == null || !hasSource((IJavaElement) element))
132         break preconditions;
133       annotate = true;
134     }
135     if (annotate) {
136       if (!annotated || force) {
137         createAnnotations(lineCoverage);
138         annotated = true;
139       }
140     } else {
141       if (annotated) {
142         clear();
143         annotated = false;
144       }
145     }
146   }
147   
148   protected boolean hasSource(IJavaElement element) {
149     if (element instanceof ISourceReference) {
150       try {
151         return ((ISourceReference) element).getSourceRange() != null;
152       } catch (JavaModelException ex) {
153         // we ignore this, the resource seems to have problems
154
}
155     }
156     return false;
157   }
158   
159   protected ILineCoverage findLineCoverage(Object JavaDoc element) {
160     // Do we have a coverage info for the editor input?
161
IJavaElementCoverage coverage = CoverageTools.getCoverageInfo(element);
162     if (coverage == null) return null;
163     
164     // Does the resource version (if any) corresponds to the coverage data?
165
IResource resource = (IResource) ((IAdaptable) element).getAdapter(IResource.class);
166     if (resource != null) {
167       if (resource.getModificationStamp() != coverage.getResourceModificationStamp()) return null;
168     }
169     
170     return coverage.getLineCoverage();
171   }
172   
173   protected void clear() {
174     AnnotationModelEvent event = new AnnotationModelEvent(this);
175     clear(event);
176     fireModelChanged(event);
177   }
178
179   protected void clear(AnnotationModelEvent event) {
180     for (Iterator JavaDoc i = annotations.iterator(); i.hasNext();) {
181       CoverageAnnotation ca = (CoverageAnnotation) i.next();
182       event.annotationRemoved(ca, ca.getPosition());
183     }
184     annotations.clear();
185   }
186
187   protected void createAnnotations(ILineCoverage linecoverage) {
188     AnnotationModelEvent event = new AnnotationModelEvent(this);
189     clear(event);
190     int firstline = linecoverage.getFirstLine();
191     int lastline = linecoverage.getLastLine();
192     int offset = linecoverage.getOffset();
193     byte[] coverage = linecoverage.getCoverage();
194     try {
195       for (int l = firstline ; l <= lastline; l++) {
196         int status = coverage[l - offset];
197         if (status != ILineCoverage.NO_CODE) {
198           IRegion region = document.getLineInformation(l - 1);
199           int docoffset = region.getOffset();
200           int doclength = region.getLength();
201           // Extend annotation for subsequent lines with same status:
202
while (l < lastline && coverage[l + 1 - offset] == status) {
203             l++;
204             region = document.getLineInformation(l - 1);
205             doclength = region.getOffset() - docoffset + region.getLength();
206           }
207           CoverageAnnotation ca = new CoverageAnnotation(docoffset, doclength, status);
208           annotations.add(ca);
209           event.annotationAdded(ca);
210         }
211       }
212     } catch (BadLocationException ex) {
213       EclEmmaUIPlugin.log(ex);
214     }
215     fireModelChanged(event);
216   }
217
218   
219   public void addAnnotationModelListener(IAnnotationModelListener listener) {
220     if (!annotationModelListeners.contains(listener)) {
221       annotationModelListeners.add(listener);
222       fireModelChanged(new AnnotationModelEvent(this, true));
223     }
224   }
225
226   public void removeAnnotationModelListener(IAnnotationModelListener listener) {
227     annotationModelListeners.remove(listener);
228   }
229   
230   protected void fireModelChanged(AnnotationModelEvent event) {
231     event.markSealed();
232     if (!event.isEmpty()) {
233       for (Iterator JavaDoc i = annotationModelListeners.iterator(); i.hasNext(); ) {
234         IAnnotationModelListener l = (IAnnotationModelListener) i.next();
235         if (l instanceof IAnnotationModelListenerExtension) {
236           ((IAnnotationModelListenerExtension) l).modelChanged(event);
237         } else {
238           l.modelChanged(this);
239         }
240       }
241     }
242   }
243
244   public void connect(IDocument document) {
245     if (this.document != document)
246       throw new RuntimeException JavaDoc("Can't connect to different document."); //$NON-NLS-1$
247
for (Iterator JavaDoc i = annotations.iterator(); i.hasNext();) {
248       CoverageAnnotation ca = (CoverageAnnotation) i.next();
249       try {
250         document.addPosition(ca.getPosition());
251       } catch (BadLocationException ex) {
252         EclEmmaUIPlugin.log(ex);
253       }
254     }
255     if (openConnections++ == 0) {
256       CoverageTools.addJavaCoverageListener(coverageListener);
257       document.addDocumentListener(documentListener);
258     }
259   }
260   
261   public void disconnect(IDocument document) {
262     if (this.document != document)
263       throw new RuntimeException JavaDoc("Can't disconnect from different document."); //$NON-NLS-1$
264
for (Iterator JavaDoc i = annotations.iterator(); i.hasNext();) {
265       CoverageAnnotation ca = (CoverageAnnotation) i.next();
266       document.removePosition(ca.getPosition());
267     }
268     if (--openConnections == 0) {
269       CoverageTools.removeJavaCoverageListener(coverageListener);
270       document.removeDocumentListener(documentListener);
271     }
272   }
273   
274   /**
275    * External modification is not supported.
276    */

277   public void addAnnotation(Annotation annotation, Position position) {
278     throw new UnsupportedOperationException JavaDoc();
279   }
280
281   /**
282    * External modification is not supported.
283    */

284   public void removeAnnotation(Annotation annotation) {
285     throw new UnsupportedOperationException JavaDoc();
286   }
287
288   public Iterator JavaDoc getAnnotationIterator() {
289     return annotations.iterator();
290   }
291
292   public Position getPosition(Annotation annotation) {
293     if (annotation instanceof CoverageAnnotation) {
294       return ((CoverageAnnotation) annotation).getPosition();
295     } else {
296       return null;
297     }
298   }
299
300 }
301
Popular Tags