KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > texteditor > AbstractMarkerAnnotationModel


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.texteditor;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.osgi.framework.Bundle;
18
19 import org.eclipse.core.resources.IMarker;
20 import org.eclipse.core.resources.IResourceStatus;
21
22 import org.eclipse.core.runtime.Assert;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IConfigurationElement;
25 import org.eclipse.core.runtime.IExtensionPoint;
26 import org.eclipse.core.runtime.ILog;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Platform;
29 import org.eclipse.core.runtime.Status;
30
31 import org.eclipse.core.filebuffers.IPersistableAnnotationModel;
32
33 import org.eclipse.jface.text.BadLocationException;
34 import org.eclipse.jface.text.IDocument;
35 import org.eclipse.jface.text.Position;
36 import org.eclipse.jface.text.source.Annotation;
37 import org.eclipse.jface.text.source.AnnotationModel;
38 import org.eclipse.jface.text.source.IAnnotationMap;
39
40 import org.eclipse.ui.editors.text.EditorsUI;
41
42 import org.eclipse.ui.PlatformUI;
43
44
45 /**
46  * Abstract implementation of a marker-based annotation model.
47  * <p>
48  * Markers are provided by an underlying source (a subclass responsibility).
49  * Markers whose textual range gets deleted during text editing are removed
50  * from the model on save. The {@link #updateMarkers(IDocument)} method can be used
51  * to force the model to update the source's markers with any changes to their
52  * locations due to edits. Clients can register a {@link org.eclipse.ui.texteditor.IMarkerUpdater}
53  * objects in order to define the process of marker updating. Registration can be done
54  * using the <code>"org.eclipse.ui.markerUpdaters"</code> extension point.
55  * </p>
56  * <p>
57  * Subclasses must implement the following methods:
58  * <ul>
59  * <li><code>retrieveMarkers</code></li>
60  * <li><code>isAcceptable</code></li>
61  * <li><code>deleteMarkers</code></li>
62  * <li><code>listenToMarkerChanges</code></li>
63  * </ul>
64  * </p>
65  */

66 public abstract class AbstractMarkerAnnotationModel extends AnnotationModel implements IPersistableAnnotationModel {
67
68     /** List of annotations whose text range became invalid because of document changes */
69     private List JavaDoc fDeletedAnnotations= new ArrayList JavaDoc(2);
70     /** List of registered and instantiated marker updaters */
71     private List JavaDoc fInstantiatedMarkerUpdaters= null;
72     /** List of registered but not yet instantiated marker updaters */
73     private List JavaDoc fMarkerUpdaterSpecifications= null;
74
75
76     /**
77      * Retrieves all markers from this model.
78      * <p>
79      * Subclasses must implement this method.</p>
80      *
81      * @return the list of markers
82      * @throws CoreException if there is a problem getting the markers
83      */

84     protected abstract IMarker[] retrieveMarkers() throws CoreException;
85
86     /**
87      * Deletes the given markers from this model.
88      * <p>
89      * Subclasses must implement this method.</p>
90      *
91      * @param markers the array of markers
92      * @throws CoreException if there are problems deleting the markers
93      */

94     protected abstract void deleteMarkers(IMarker[] markers) throws CoreException;
95
96     /**
97      * Tells the model whether it should listen for marker changes.
98      * <p>
99      * Subclasses must implement this method.</p>
100      *
101      * @param listen <code>true</code> if this model should listen, and
102      * <code>false</code> otherwise
103      */

104     protected abstract void listenToMarkerChanges(boolean listen);
105
106     /**
107      * Determines whether the marker is acceptable as an addition to this model.
108      * If the marker, say, represents an aspect or range of no interest to this
109      * model, the marker is rejected.
110      * <p>
111      * Subclasses must implement this method.</p>
112      *
113      * @param marker the marker
114      * @return <code>true</code> if the marker is acceptable
115      */

116     protected abstract boolean isAcceptable(IMarker marker);
117
118     /**
119      * Creates a new annotation model. The annotation model does not manage any
120      * annotations and is not connected to any document.
121      */

122     protected AbstractMarkerAnnotationModel() {
123     }
124
125     /**
126      * Adds the given marker updater to this annotation model.
127      * It is the client's responsibility to ensure the consistency
128      * of the set of registered marker updaters.
129      *
130      * @param markerUpdater the marker updater to be added
131      */

132     protected void addMarkerUpdater(IMarkerUpdater markerUpdater) {
133         if (!fInstantiatedMarkerUpdaters.contains(markerUpdater))
134             fInstantiatedMarkerUpdaters.add(markerUpdater);
135     }
136
137     /**
138      * Removes the given marker updater from this annotation model.
139      *
140      * @param markerUpdater the marker updater to be removed
141      */

142     protected void removeMarkerUpdater(IMarkerUpdater markerUpdater) {
143         fInstantiatedMarkerUpdaters.remove(markerUpdater);
144     }
145
146     /**
147      * Creates a new annotation for the given marker.
148      * <p>
149      * Subclasses may override.</p>
150      *
151      * @param marker the marker
152      * @return the new marker annotation
153      */

154     protected MarkerAnnotation createMarkerAnnotation(IMarker marker) {
155         return new MarkerAnnotation(marker);
156     }
157
158     /**
159      * Handles an unanticipated <code>CoreException</code> in
160      * a standard manner.
161      *
162      * @param exception the exception
163      * @param message a message to aid debugging
164      */

165     protected void handleCoreException(CoreException exception, String JavaDoc message) {
166
167         Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
168         ILog log= Platform.getLog(bundle);
169         if (message != null)
170             log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, message, exception));
171         else
172             log.log(exception.getStatus());
173     }
174
175     /**
176      * Creates and returns the character position of the given marker based
177      * on its attributes.
178      * <p>
179      * Subclasses may override.</p>
180      *
181      * @param marker the marker
182      * @return the new position or <code>null</code> if the marker attributes do not specify a valid position
183      */

184     protected Position createPositionFromMarker(IMarker marker) {
185
186         int start= MarkerUtilities.getCharStart(marker);
187         int end= MarkerUtilities.getCharEnd(marker);
188
189         if (start > end) {
190             end= start + end;
191             start= end - start;
192             end= end - start;
193         }
194
195         if (start == -1 && end == -1) {
196             // marker line number is 1-based
197
int line= MarkerUtilities.getLineNumber(marker);
198             if (line > 0 && fDocument != null) {
199                 try {
200                     start= fDocument.getLineOffset(line - 1);
201                     end= start;
202                 } catch (BadLocationException x) {
203                 }
204             }
205         }
206
207         if (start > -1 && end > -1)
208             return new Position(start, end - start);
209
210         return null;
211     }
212
213     /**
214      * Creates an annotation for the given marker and adds it to this model.
215      * Does nothing if the marker is not acceptable to this model.
216      *
217      * @param marker the marker
218      * @see #isAcceptable(IMarker)
219      */

220     protected final void addMarkerAnnotation(IMarker marker) {
221
222         if (isAcceptable(marker)) {
223             Position p= createPositionFromMarker(marker);
224             if (p != null)
225                 try {
226                     MarkerAnnotation annotation= createMarkerAnnotation(marker);
227                     if (annotation != null)
228                         addAnnotation(annotation, p, false);
229                 } catch (BadLocationException e) {
230                     // ignore invalid position
231
}
232         }
233     }
234
235     /**
236      * Connects to the source of markers as marker change listener.
237      * @see AnnotationModel#connected()
238      */

239     protected void connected() {
240
241         listenToMarkerChanges(true);
242
243         try {
244             catchupWithMarkers();
245         } catch (CoreException x) {
246             if (x.getStatus().getCode() != IResourceStatus.RESOURCE_NOT_FOUND)
247                 handleCoreException(x, TextEditorMessages.AbstractMarkerAnnotationModel_connected);
248         }
249
250         fireModelChanged();
251     }
252
253     /**
254      * Installs all marker updaters for this marker annotation model.
255      */

256     private void installMarkerUpdaters() {
257
258         // initialize lists - indicates that the initialization happened
259
fMarkerUpdaterSpecifications= new ArrayList JavaDoc(2);
260         fInstantiatedMarkerUpdaters= new ArrayList JavaDoc(2);
261
262         // populate list
263
IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(EditorsUI.PLUGIN_ID, "markerUpdaters"); //$NON-NLS-1$
264
if (extensionPoint != null) {
265             IConfigurationElement[] elements= extensionPoint.getConfigurationElements();
266             for (int i= 0; i < elements.length; i++)
267                 fMarkerUpdaterSpecifications.add(elements[i]);
268         }
269     }
270
271     /**
272      * Uninstalls all marker updaters.
273      */

274     private void uninstallMarkerUpdaters() {
275         if (fInstantiatedMarkerUpdaters != null) {
276             fInstantiatedMarkerUpdaters.clear();
277             fInstantiatedMarkerUpdaters= null;
278         }
279
280         if (fMarkerUpdaterSpecifications != null) {
281             fMarkerUpdaterSpecifications.clear();
282             fMarkerUpdaterSpecifications= null;
283         }
284     }
285
286     /**
287      * Removes the marker change listener.
288      * @see AnnotationModel#disconnected()
289      */

290     protected void disconnected() {
291         listenToMarkerChanges(false);
292         uninstallMarkerUpdaters();
293     }
294
295     /**
296      * Returns the position known to this annotation model for the given marker.
297      *
298      * @param marker the marker
299      * @return the position, or <code>null</code> if none
300      */

301     public Position getMarkerPosition(IMarker marker) {
302         MarkerAnnotation a= getMarkerAnnotation(marker);
303         if (a != null) {
304             return (Position) getAnnotationMap().get(a);
305         }
306         return null;
307     }
308
309     /**
310      * Updates the annotation corresponding to the given marker which has changed
311      * in some way.
312      * <p>
313      * Subclasses may override.</p>
314      *
315      * @param marker the marker
316      */

317     protected void modifyMarkerAnnotation(IMarker marker) {
318         MarkerAnnotation a= getMarkerAnnotation(marker);
319         if (a != null) {
320             Position p= createPositionFromMarker(marker);
321             if (p != null) {
322                 a.update();
323                 modifyAnnotationPosition(a, p, false);
324             }
325         } else
326             addMarkerAnnotation(marker);
327     }
328
329     /*
330      * @see AnnotationModel#removeAnnotations(List, boolean, boolean)
331      */

332     protected void removeAnnotations(List JavaDoc annotations, boolean fireModelChanged, boolean modelInitiated) {
333         if (annotations != null && annotations.size() > 0) {
334
335             List JavaDoc markerAnnotations= new ArrayList JavaDoc();
336             for (Iterator JavaDoc e= annotations.iterator(); e.hasNext();) {
337                 Annotation a= (Annotation) e.next();
338                 if (a instanceof MarkerAnnotation)
339                     markerAnnotations.add(a);
340
341                 // remove annotations from annotation model
342
removeAnnotation(a, false);
343             }
344
345             if (markerAnnotations.size() > 0) {
346
347                 if (modelInitiated) {
348                     // if model initiated also remove it from the marker manager
349

350                     listenToMarkerChanges(false);
351                     try {
352
353                         IMarker[] m= new IMarker[markerAnnotations.size()];
354                         for (int i= 0; i < m.length; i++) {
355                             MarkerAnnotation ma = (MarkerAnnotation) markerAnnotations.get(i);
356                             m[i]= ma.getMarker();
357                         }
358                         deleteMarkers(m);
359
360                     } catch (CoreException x) {
361                         handleCoreException(x, TextEditorMessages.AbstractMarkerAnnotationModel_removeAnnotations);
362                     }
363                     listenToMarkerChanges(true);
364
365                 } else {
366                     // remember deleted annotations in order to remove their markers later on
367
fDeletedAnnotations.addAll(markerAnnotations);
368                 }
369             }
370
371             if (fireModelChanged)
372                 fireModelChanged();
373         }
374     }
375
376     /**
377      * Removes the annotation corresponding to the given marker. Does nothing
378      * if there is no annotation for this marker.
379      *
380      * @param marker the marker
381      */

382     protected final void removeMarkerAnnotation(IMarker marker) {
383         MarkerAnnotation a= getMarkerAnnotation(marker);
384         if (a != null) {
385             removeAnnotation(a, false);
386         }
387     }
388
389     /**
390      * Re-populates this model with annotations for all markers retrieved
391      * from the maker source via <code>retrieveMarkers</code>.
392      *
393      * @throws CoreException if there is a problem getting the markers
394      */

395     private void catchupWithMarkers() throws CoreException {
396
397         for (Iterator JavaDoc e=getAnnotationIterator(false); e.hasNext();) {
398             Annotation a= (Annotation) e.next();
399             if (a instanceof MarkerAnnotation)
400                 removeAnnotation(a, false);
401         }
402
403         IMarker[] markers= retrieveMarkers();
404         if (markers != null) {
405             for (int i= 0; i < markers.length; i++)
406                 addMarkerAnnotation(markers[i]);
407         }
408     }
409
410     /**
411      * Returns this model's annotation for the given marker.
412      *
413      * @param marker the marker
414      * @return the annotation, or <code>null</code> if none
415      */

416     public final MarkerAnnotation getMarkerAnnotation(IMarker marker) {
417         Iterator JavaDoc e= getAnnotationIterator(false);
418         while (e.hasNext()) {
419             Object JavaDoc o= e.next();
420             if (o instanceof MarkerAnnotation) {
421                 MarkerAnnotation a= (MarkerAnnotation) o;
422                 if (marker.equals(a.getMarker())) {
423                     return a;
424                 }
425             }
426         }
427         return null;
428     }
429
430     /**
431      * Creates a marker updater as specified in the given configuration element.
432      *
433      * @param element the configuration element
434      * @return the created marker updater or <code>null</code> if none could be created
435      */

436     private IMarkerUpdater createMarkerUpdater(IConfigurationElement element) {
437         try {
438             return (IMarkerUpdater) element.createExecutableExtension("class"); //$NON-NLS-1$
439
} catch (CoreException x) {
440             handleCoreException(x, TextEditorMessages.AbstractMarkerAnnotationModel_createMarkerUpdater);
441         }
442
443         return null;
444     }
445
446     /**
447      * Checks whether a marker updater is registered for the type of the
448      * given marker but not yet instantiated. If so, the method instantiates
449      * the marker updater and registers it with this model.
450      *
451      * @param marker the marker for which to look for an updater
452      * @since 2.0
453      */

454     private void checkMarkerUpdaters(IMarker marker) {
455         List JavaDoc toBeDeleted= new ArrayList JavaDoc();
456         for (int i= 0; i < fMarkerUpdaterSpecifications.size(); i++) {
457             IConfigurationElement spec= (IConfigurationElement) fMarkerUpdaterSpecifications.get(i);
458             String JavaDoc markerType= spec.getAttribute("markerType"); //$NON-NLS-1$
459
if (markerType == null || MarkerUtilities.isMarkerType(marker, markerType)) {
460                 toBeDeleted.add(spec);
461                 IMarkerUpdater updater= createMarkerUpdater(spec);
462                 if (updater != null)
463                     addMarkerUpdater(updater);
464             }
465         }
466
467         for (int i= 0; i < toBeDeleted.size(); i++)
468             fMarkerUpdaterSpecifications.remove(toBeDeleted.get(i));
469     }
470
471     /**
472      * Updates the given marker according to the given position in the given
473      * document. If the given position is <code>null</code>, the marker is
474      * assumed to carry the correct positional information. If it is detected
475      * that the marker is invalid and should thus be deleted, this method
476      * returns <code>false</code>.
477      * <p>
478      * <strong>Note:</strong> This implementation queries the registered
479      * {@linkplain IMarkerUpdater}s. If any of these updaters returns
480      * <code>false</code> this method also returns <code>false</code>.
481      * </p>
482      *
483      * @param marker the marker to be updated
484      * @param document the document into which the given position points
485      * @param position the current position of the marker inside the given document
486      * @return <code>false</code> if the marker is invalid
487      * @throws CoreException if there is a problem updating the marker
488      * @since 2.0
489      * @deprecated use <code>updateMarker(IDocument, IMarker, Position)</code> instead. This method will be changed to protected.
490      */

491     public boolean updateMarker(IMarker marker, IDocument document, Position position) throws CoreException {
492
493         if (fMarkerUpdaterSpecifications == null)
494             installMarkerUpdaters();
495
496         if (!fMarkerUpdaterSpecifications.isEmpty())
497             checkMarkerUpdaters(marker);
498
499         boolean isOK= true;
500
501         for (int i= 0; i < fInstantiatedMarkerUpdaters.size(); i++) {
502             IMarkerUpdater updater= (IMarkerUpdater) fInstantiatedMarkerUpdaters.get(i);
503             String JavaDoc markerType= updater.getMarkerType();
504             if (markerType == null || MarkerUtilities.isMarkerType(marker, markerType)) {
505
506                 if (position == null) {
507                     /* compatibility code */
508                     position= createPositionFromMarker(marker);
509                 }
510
511                 isOK= (isOK && updater.updateMarker(marker, document, position));
512             }
513         }
514
515         return isOK;
516     }
517
518     /**
519      * Updates the given marker according to the given position in the given
520      * document. If the given position is <code>null</code>, the marker is
521      * assumed to carry the correct positional information. If it is detected
522      * that the marker is invalid and should thus be deleted, this method
523      * returns <code>false</code>.
524      *
525      * @param marker the marker to be updated
526      * @param document the document into which the given position points
527      * @param position the current position of the marker inside the given document
528      * @return <code>false</code> if the marker is invalid
529      * @throws CoreException if there is a problem updating the marker
530      * @since 3.0
531      */

532     public boolean updateMarker(IDocument document, IMarker marker, Position position) throws CoreException {
533         listenToMarkerChanges(false);
534         try {
535             return updateMarker(marker, document, position);
536         } finally {
537             listenToMarkerChanges(true);
538         }
539     }
540
541     /**
542      * Updates the markers managed by this annotation model by calling
543      * all registered marker updaters (<code>IMarkerUpdater</code>).
544      *
545      * @param document the document to which this model is currently connected
546      * @throws CoreException if there is a problem updating the markers
547      */

548     public void updateMarkers(IDocument document) throws CoreException {
549
550         Assert.isTrue(fDocument == document);
551
552         IAnnotationMap annotationMap= getAnnotationMap();
553
554         if (annotationMap.size() == 0 && fDeletedAnnotations.size() == 0)
555             return;
556
557         if (fMarkerUpdaterSpecifications == null)
558             installMarkerUpdaters();
559
560         listenToMarkerChanges(false);
561
562         try {
563
564             // update all markers with the positions known by the annotation model
565
for (Iterator JavaDoc e= getAnnotationIterator(false); e.hasNext();) {
566                 Object JavaDoc o= e.next();
567                 if (o instanceof MarkerAnnotation) {
568                     MarkerAnnotation a= (MarkerAnnotation) o;
569                     IMarker marker= a.getMarker();
570                     Position position= (Position) annotationMap.get(a);
571                     if ( !updateMarker(marker, document, position)) {
572                         if ( !fDeletedAnnotations.contains(a))
573                             fDeletedAnnotations.add(a);
574                     }
575                 }
576             }
577
578             if (!fDeletedAnnotations.isEmpty()) {
579                 removeAnnotations(fDeletedAnnotations, true, true);
580                 fDeletedAnnotations.clear();
581             }
582
583         } finally {
584
585             listenToMarkerChanges(true);
586
587         }
588     }
589
590     /**
591      * Resets all the markers to their original state.
592      */

593     public void resetMarkers() {
594
595         // re-initializes the positions from the markers
596
for (Iterator JavaDoc e= getAnnotationIterator(false); e.hasNext();) {
597             Object JavaDoc o= e.next();
598             if (o instanceof MarkerAnnotation) {
599                 MarkerAnnotation a= (MarkerAnnotation) o;
600                 Position p= createPositionFromMarker(a.getMarker());
601                 if (p != null) {
602                     removeAnnotation(a, false);
603                     try {
604                         addAnnotation(a, p, false);
605                     } catch (BadLocationException e1) {
606                         // ignore invalid position
607
}
608                 }
609             }
610         }
611
612         // add the markers of deleted positions back to the annotation model
613
for (Iterator JavaDoc e= fDeletedAnnotations.iterator(); e.hasNext();) {
614             Object JavaDoc o= e.next();
615             if (o instanceof MarkerAnnotation) {
616                 MarkerAnnotation a= (MarkerAnnotation) o;
617                 Position p= createPositionFromMarker(a.getMarker());
618                 if (p != null)
619                     try {
620                         addAnnotation(a, p, false);
621                     } catch (BadLocationException e1) {
622                         // ignore invalid position
623
}
624             }
625         }
626         fDeletedAnnotations.clear();
627
628         // fire annotation model changed
629
fireModelChanged();
630     }
631
632     /*
633      * @see org.eclipse.jface.text.source.IPersistableAnnotationModel#commit(org.eclipse.jface.text.IDocument)
634      */

635     public void commit(IDocument document) throws CoreException {
636         updateMarkers(document);
637     }
638
639     /*
640      * @see org.eclipse.jface.text.source.IPersistableAnnotationModel#revert(org.eclipse.jface.text.IDocument)
641      */

642     public void revert(IDocument document) {
643         resetMarkers();
644     }
645
646     /*
647      * @see org.eclipse.jface.text.source.IPersistableAnnotationModel#reinitialize(org.eclipse.jface.text.IDocument)
648      */

649     public void reinitialize(IDocument document) {
650         resetMarkers();
651     }
652 }
653
Popular Tags