129
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2008 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 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module dwtx.jface.text.source.AnnotationModel;
|
|
14
|
131
|
15 import dwtx.jface.text.source.ISharedTextColors; // packageimport
|
|
16 import dwtx.jface.text.source.ILineRange; // packageimport
|
|
17 import dwtx.jface.text.source.IAnnotationPresentation; // packageimport
|
|
18 import dwtx.jface.text.source.IVerticalRulerInfoExtension; // packageimport
|
|
19 import dwtx.jface.text.source.ICharacterPairMatcher; // packageimport
|
|
20 import dwtx.jface.text.source.TextInvocationContext; // packageimport
|
|
21 import dwtx.jface.text.source.LineChangeHover; // packageimport
|
|
22 import dwtx.jface.text.source.IChangeRulerColumn; // packageimport
|
|
23 import dwtx.jface.text.source.IAnnotationMap; // packageimport
|
|
24 import dwtx.jface.text.source.IAnnotationModelListenerExtension; // packageimport
|
|
25 import dwtx.jface.text.source.ISourceViewerExtension2; // packageimport
|
|
26 import dwtx.jface.text.source.IAnnotationHover; // packageimport
|
|
27 import dwtx.jface.text.source.ContentAssistantFacade; // packageimport
|
|
28 import dwtx.jface.text.source.IAnnotationAccess; // packageimport
|
|
29 import dwtx.jface.text.source.IVerticalRulerExtension; // packageimport
|
|
30 import dwtx.jface.text.source.IVerticalRulerColumn; // packageimport
|
|
31 import dwtx.jface.text.source.LineNumberRulerColumn; // packageimport
|
|
32 import dwtx.jface.text.source.MatchingCharacterPainter; // packageimport
|
|
33 import dwtx.jface.text.source.IAnnotationModelExtension; // packageimport
|
|
34 import dwtx.jface.text.source.ILineDifferExtension; // packageimport
|
|
35 import dwtx.jface.text.source.DefaultCharacterPairMatcher; // packageimport
|
|
36 import dwtx.jface.text.source.LineNumberChangeRulerColumn; // packageimport
|
|
37 import dwtx.jface.text.source.IAnnotationAccessExtension; // packageimport
|
|
38 import dwtx.jface.text.source.ISourceViewer; // packageimport
|
|
39 import dwtx.jface.text.source.ILineDifferExtension2; // packageimport
|
|
40 import dwtx.jface.text.source.IAnnotationModelListener; // packageimport
|
|
41 import dwtx.jface.text.source.IVerticalRuler; // packageimport
|
|
42 import dwtx.jface.text.source.DefaultAnnotationHover; // packageimport
|
|
43 import dwtx.jface.text.source.SourceViewer; // packageimport
|
|
44 import dwtx.jface.text.source.SourceViewerConfiguration; // packageimport
|
|
45 import dwtx.jface.text.source.AnnotationBarHoverManager; // packageimport
|
|
46 import dwtx.jface.text.source.CompositeRuler; // packageimport
|
|
47 import dwtx.jface.text.source.ImageUtilities; // packageimport
|
|
48 import dwtx.jface.text.source.VisualAnnotationModel; // packageimport
|
|
49 import dwtx.jface.text.source.IAnnotationModel; // packageimport
|
|
50 import dwtx.jface.text.source.ISourceViewerExtension3; // packageimport
|
|
51 import dwtx.jface.text.source.ILineDiffInfo; // packageimport
|
|
52 import dwtx.jface.text.source.VerticalRulerEvent; // packageimport
|
|
53 import dwtx.jface.text.source.ChangeRulerColumn; // packageimport
|
|
54 import dwtx.jface.text.source.ILineDiffer; // packageimport
|
|
55 import dwtx.jface.text.source.AnnotationModelEvent; // packageimport
|
|
56 import dwtx.jface.text.source.AnnotationColumn; // packageimport
|
|
57 import dwtx.jface.text.source.AnnotationRulerColumn; // packageimport
|
|
58 import dwtx.jface.text.source.IAnnotationHoverExtension; // packageimport
|
|
59 import dwtx.jface.text.source.AbstractRulerColumn; // packageimport
|
|
60 import dwtx.jface.text.source.ISourceViewerExtension; // packageimport
|
|
61 import dwtx.jface.text.source.AnnotationMap; // packageimport
|
|
62 import dwtx.jface.text.source.IVerticalRulerInfo; // packageimport
|
|
63 import dwtx.jface.text.source.IAnnotationModelExtension2; // packageimport
|
|
64 import dwtx.jface.text.source.LineRange; // packageimport
|
|
65 import dwtx.jface.text.source.IAnnotationAccessExtension2; // packageimport
|
|
66 import dwtx.jface.text.source.VerticalRuler; // packageimport
|
|
67 import dwtx.jface.text.source.JFaceTextMessages; // packageimport
|
|
68 import dwtx.jface.text.source.IOverviewRuler; // packageimport
|
|
69 import dwtx.jface.text.source.Annotation; // packageimport
|
|
70 import dwtx.jface.text.source.IVerticalRulerListener; // packageimport
|
|
71 import dwtx.jface.text.source.ISourceViewerExtension4; // packageimport
|
|
72 import dwtx.jface.text.source.AnnotationPainter; // packageimport
|
|
73 import dwtx.jface.text.source.IAnnotationHoverExtension2; // packageimport
|
|
74 import dwtx.jface.text.source.OverviewRuler; // packageimport
|
|
75 import dwtx.jface.text.source.OverviewRulerHoverManager; // packageimport
|
|
76
|
|
77
|
129
|
78 import dwt.dwthelper.utils;
|
|
79
|
|
80 import java.util.ArrayList;
|
|
81 import java.util.HashMap;
|
|
82 import java.util.IdentityHashMap;
|
|
83 import java.util.Iterator;
|
|
84 import java.util.List;
|
|
85 import java.util.Map;
|
|
86 import java.util.NoSuchElementException;
|
|
87
|
|
88 import dwtx.core.runtime.Assert;
|
|
89 import dwtx.jface.text.AbstractDocument;
|
|
90 import dwtx.jface.text.BadLocationException;
|
|
91 import dwtx.jface.text.BadPositionCategoryException;
|
|
92 import dwtx.jface.text.DocumentEvent;
|
|
93 import dwtx.jface.text.IDocument;
|
|
94 import dwtx.jface.text.IDocumentListener;
|
|
95 import dwtx.jface.text.ISynchronizable;
|
|
96 import dwtx.jface.text.Position;
|
|
97
|
|
98
|
|
99 /**
|
|
100 * Standard implementation of {@link IAnnotationModel} and its extension
|
|
101 * interfaces. This class can directly be used by clients. Subclasses may adapt
|
|
102 * this annotation model to other existing annotation mechanisms. This class
|
|
103 * also implements {@link dwtx.jface.text.ISynchronizable}. All
|
|
104 * modifications of the model's internal annotation map are synchronized using
|
|
105 * the model's lock object.
|
|
106 */
|
|
107 public class AnnotationModel : IAnnotationModel, IAnnotationModelExtension, IAnnotationModelExtension2, ISynchronizable {
|
|
108
|
|
109
|
|
110 /**
|
|
111 * Iterator that returns the annotations for a given region.
|
|
112 *
|
|
113 * @since 3.4
|
|
114 * @see AnnotationModel.RegionIterator#RegionIterator(Iterator, IAnnotationModel, int, int, bool, bool)
|
|
115 */
|
|
116 private static final class RegionIterator : Iterator {
|
|
117
|
|
118 private final Iterator fParentIterator;
|
|
119 private final bool fCanEndAfter;
|
|
120 private final bool fCanStartBefore;
|
|
121 private final IAnnotationModel fModel;
|
|
122 private Object fNext;
|
|
123 private Position fRegion;
|
|
124
|
|
125 /**
|
|
126 * Iterator that returns all annotations from the parent iterator which
|
|
127 * have a position in the given model inside the given region.
|
|
128 * <p>
|
|
129 * See {@link IAnnotationModelExtension2} for a definition of inside.
|
|
130 * </p>
|
|
131 *
|
|
132 * @param parentIterator iterator containing all annotations
|
|
133 * @param model the model to use to retrieve positions from for each
|
|
134 * annotation
|
|
135 * @param offset start position of the region
|
|
136 * @param length length of the region
|
|
137 * @param canStartBefore include annotations starting before region
|
|
138 * @param canEndAfter include annotations ending after region
|
|
139 * @see IAnnotationModelExtension2
|
|
140 */
|
|
141 public RegionIterator(Iterator parentIterator, IAnnotationModel model, int offset, int length, bool canStartBefore, bool canEndAfter) {
|
|
142 fParentIterator= parentIterator;
|
|
143 fModel= model;
|
|
144 fRegion= new Position(offset, length);
|
|
145 fCanEndAfter= canEndAfter;
|
|
146 fCanStartBefore= canStartBefore;
|
|
147 fNext= findNext();
|
|
148 }
|
|
149
|
|
150 /*
|
|
151 * @see java.util.Iterator#hasNext()
|
|
152 */
|
|
153 public bool hasNext() {
|
|
154 return fNext !is null;
|
|
155 }
|
|
156
|
|
157 /*
|
|
158 * @see java.util.Iterator#next()
|
|
159 */
|
|
160 public Object next() {
|
|
161 if (!hasNext())
|
|
162 throw new NoSuchElementException();
|
|
163
|
|
164 Object result= fNext;
|
|
165 fNext= findNext();
|
|
166 return result;
|
|
167 }
|
|
168
|
|
169 /*
|
|
170 * @see java.util.Iterator#remove()
|
|
171 */
|
|
172 public void remove() {
|
|
173 throw new UnsupportedOperationException();
|
|
174 }
|
|
175
|
|
176 private Object findNext() {
|
|
177 while (fParentIterator.hasNext()) {
|
|
178 Annotation next= (Annotation) fParentIterator.next();
|
|
179 Position position= fModel.getPosition(next);
|
|
180 if (position !is null) {
|
|
181 int offset= position.getOffset();
|
|
182 if (isWithinRegion(offset, position.getLength()))
|
|
183 return next;
|
|
184 }
|
|
185 }
|
|
186 return null;
|
|
187 }
|
|
188
|
|
189 private bool isWithinRegion(int start, int length) {
|
|
190 if (fCanStartBefore && fCanEndAfter)
|
|
191 return fRegion.overlapsWith(start, length);
|
|
192 else if (fCanStartBefore)
|
|
193 return fRegion.includes(start + length - 1);
|
|
194 else if (fCanEndAfter)
|
|
195 return fRegion.includes(start);
|
|
196 else
|
|
197 return fRegion.includes(start) && fRegion.includes(start + length - 1);
|
|
198 }
|
|
199 }
|
|
200
|
|
201 /**
|
|
202 * An iterator iteration over a Positions and mapping positions to
|
|
203 * annotations using a provided map if the provided map contains the element.
|
|
204 *
|
|
205 * @since 3.4
|
|
206 */
|
|
207 private static final class AnnotationsInterator : Iterator {
|
|
208
|
|
209 private Object fNext;
|
|
210 private final Position[] fPositions;
|
|
211 private int fIndex;
|
|
212 private final Map fMap;
|
|
213
|
|
214 /**
|
|
215 * @param positions positions to iterate over
|
|
216 * @param map a map to map positions to annotations
|
|
217 */
|
|
218 public AnnotationsInterator(Position[] positions, Map map) {
|
|
219 fPositions= positions;
|
|
220 fIndex= 0;
|
|
221 fMap= map;
|
|
222 fNext= findNext();
|
|
223 }
|
|
224
|
|
225 /* (non-Javadoc)
|
|
226 * @see java.util.Iterator#hasNext()
|
|
227 */
|
|
228 public bool hasNext() {
|
|
229 return fNext !is null;
|
|
230 }
|
|
231
|
|
232 /* (non-Javadoc)
|
|
233 * @see java.util.Iterator#next()
|
|
234 */
|
|
235 public Object next() {
|
|
236 Object result= fNext;
|
|
237 fNext= findNext();
|
|
238 return result;
|
|
239 }
|
|
240
|
|
241 /* (non-Javadoc)
|
|
242 * @see java.util.Iterator#remove()
|
|
243 */
|
|
244 public void remove() {
|
|
245 throw new UnsupportedOperationException();
|
|
246 }
|
|
247
|
|
248 private Object findNext() {
|
|
249 while (fIndex < fPositions.length) {
|
|
250 Position position= fPositions[fIndex];
|
|
251 fIndex++;
|
|
252 if (fMap.containsKey(position))
|
|
253 return fMap.get(position);
|
|
254 }
|
|
255
|
|
256 return null;
|
|
257 }
|
|
258 }
|
|
259
|
|
260 /**
|
|
261 * A single iterator builds its behavior based on a sequence of iterators.
|
|
262 *
|
|
263 * @since 3.1
|
|
264 */
|
|
265 private static class MetaIterator : Iterator {
|
|
266
|
|
267 /** The iterator over a list of iterators. */
|
|
268 private Iterator fSuperIterator;
|
|
269 /** The current iterator. */
|
|
270 private Iterator fCurrent;
|
|
271 /** The current element. */
|
|
272 private Object fCurrentElement;
|
|
273
|
|
274
|
|
275 public MetaIterator(Iterator iterator) {
|
|
276 fSuperIterator= iterator;
|
|
277 fCurrent= (Iterator) fSuperIterator.next(); // there is at least one.
|
|
278 }
|
|
279
|
|
280 public void remove() {
|
|
281 throw new UnsupportedOperationException();
|
|
282 }
|
|
283
|
|
284 public bool hasNext() {
|
|
285 if (fCurrentElement !is null)
|
|
286 return true;
|
|
287
|
|
288 if (fCurrent.hasNext()) {
|
|
289 fCurrentElement= fCurrent.next();
|
|
290 return true;
|
|
291 } else if (fSuperIterator.hasNext()) {
|
|
292 fCurrent= (Iterator) fSuperIterator.next();
|
|
293 return hasNext();
|
|
294 } else
|
|
295 return false;
|
|
296 }
|
|
297
|
|
298 public Object next() {
|
|
299 if (!hasNext())
|
|
300 throw new NoSuchElementException();
|
|
301
|
|
302 Object element= fCurrentElement;
|
|
303 fCurrentElement= null;
|
|
304 return element;
|
|
305 }
|
|
306 }
|
|
307
|
|
308 /**
|
|
309 * Internal annotation model listener for forwarding annotation model changes from the attached models to the
|
|
310 * registered listeners of the outer most annotation model.
|
|
311 *
|
|
312 * @since 3.0
|
|
313 */
|
|
314 private class InternalModelListener : IAnnotationModelListener, IAnnotationModelListenerExtension {
|
|
315
|
|
316 /*
|
|
317 * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel)
|
|
318 */
|
|
319 public void modelChanged(IAnnotationModel model) {
|
|
320 AnnotationModel.this.fireModelChanged(new AnnotationModelEvent(model, true));
|
|
321 }
|
|
322
|
|
323 /*
|
|
324 * @see dwtx.jface.text.source.IAnnotationModelListenerExtension#modelChanged(dwtx.jface.text.source.AnnotationModelEvent)
|
|
325 */
|
|
326 public void modelChanged(AnnotationModelEvent event) {
|
|
327 AnnotationModel.this.fireModelChanged(event);
|
|
328 }
|
|
329 }
|
|
330
|
|
331 /**
|
|
332 * The list of managed annotations
|
|
333 * @deprecated since 3.0 use <code>getAnnotationMap</code> instead
|
|
334 */
|
|
335 protected Map fAnnotations;
|
|
336 /**
|
|
337 * The map which maps {@link Position} to {@link Annotation}.
|
|
338 * @since 3.4
|
|
339 **/
|
|
340 private IdentityHashMap fPositions;
|
|
341 /** The list of annotation model listeners */
|
|
342 protected ArrayList fAnnotationModelListeners;
|
|
343 /** The document connected with this model */
|
|
344 protected IDocument fDocument;
|
|
345 /** The number of open connections to the same document */
|
|
346 private int fOpenConnections= 0;
|
|
347 /** The document listener for tracking whether document positions might have been changed. */
|
|
348 private IDocumentListener fDocumentListener;
|
|
349 /** The flag indicating whether the document positions might have been changed. */
|
|
350 private bool fDocumentChanged= true;
|
|
351 /**
|
|
352 * The model's attachment.
|
|
353 * @since 3.0
|
|
354 */
|
|
355 private Map fAttachments= new HashMap();
|
|
356 /**
|
|
357 * The annotation model listener on attached sub-models.
|
|
358 * @since 3.0
|
|
359 */
|
|
360 private IAnnotationModelListener fModelListener= new InternalModelListener();
|
|
361 /**
|
|
362 * The current annotation model event.
|
|
363 * @since 3.0
|
|
364 */
|
|
365 private AnnotationModelEvent fModelEvent;
|
|
366 /**
|
|
367 * The modification stamp.
|
|
368 * @since 3.0
|
|
369 */
|
|
370 private Object fModificationStamp= new Object();
|
|
371
|
|
372 /**
|
|
373 * Creates a new annotation model. The annotation is empty, i.e. does not
|
|
374 * manage any annotations and is not connected to any document.
|
|
375 */
|
|
376 public AnnotationModel() {
|
|
377 fAnnotations= new AnnotationMap(10);
|
|
378 fPositions= new IdentityHashMap(10);
|
|
379 fAnnotationModelListeners= new ArrayList(2);
|
|
380
|
|
381 fDocumentListener= new IDocumentListener() {
|
|
382
|
|
383 public void documentAboutToBeChanged(DocumentEvent event) {
|
|
384 }
|
|
385
|
|
386 public void documentChanged(DocumentEvent event) {
|
|
387 fDocumentChanged= true;
|
|
388 }
|
|
389 };
|
|
390 }
|
|
391
|
|
392 /**
|
|
393 * Returns the annotation map internally used by this annotation model.
|
|
394 *
|
|
395 * @return the annotation map internally used by this annotation model
|
|
396 * @since 3.0
|
|
397 */
|
|
398 protected IAnnotationMap getAnnotationMap() {
|
|
399 return (IAnnotationMap) fAnnotations;
|
|
400 }
|
|
401
|
|
402 /*
|
|
403 * @see dwtx.jface.text.ISynchronizable#getLockObject()
|
|
404 * @since 3.0
|
|
405 */
|
|
406 public Object getLockObject() {
|
|
407 return getAnnotationMap().getLockObject();
|
|
408 }
|
|
409
|
|
410 /*
|
|
411 * @see dwtx.jface.text.ISynchronizable#setLockObject(java.lang.Object)
|
|
412 * @since 3.0
|
|
413 */
|
|
414 public void setLockObject(Object lockObject) {
|
|
415 getAnnotationMap().setLockObject(lockObject);
|
|
416 }
|
|
417
|
|
418 /**
|
|
419 * Returns the current annotation model event. This is the event that will be sent out
|
|
420 * when calling <code>fireModelChanged</code>.
|
|
421 *
|
|
422 * @return the current annotation model event
|
|
423 * @since 3.0
|
|
424 */
|
|
425 protected final AnnotationModelEvent getAnnotationModelEvent() {
|
|
426 synchronized (getLockObject()) {
|
|
427 if (fModelEvent is null) {
|
|
428 fModelEvent= createAnnotationModelEvent();
|
|
429 fModelEvent.markWorldChange(false);
|
|
430 fModificationStamp= fModelEvent;
|
|
431 }
|
|
432 return fModelEvent;
|
|
433 }
|
|
434 }
|
|
435
|
|
436 /*
|
|
437 * @see dwtx.jface.text.source.IAnnotationModel#addAnnotation(dwtx.jface.text.source.Annotation, dwtx.jface.text.Position)
|
|
438 */
|
|
439 public void addAnnotation(Annotation annotation, Position position) {
|
|
440 try {
|
|
441 addAnnotation(annotation, position, true);
|
|
442 } catch (BadLocationException e) {
|
|
443 // ignore invalid position
|
|
444 }
|
|
445 }
|
|
446
|
|
447 /*
|
|
448 * @see dwtx.jface.text.source.IAnnotationModelExtension#replaceAnnotations(dwtx.jface.text.source.Annotation[], java.util.Map)
|
|
449 * @since 3.0
|
|
450 */
|
|
451 public void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd) {
|
|
452 try {
|
|
453 replaceAnnotations(annotationsToRemove, annotationsToAdd, true);
|
|
454 } catch (BadLocationException x) {
|
|
455 }
|
|
456 }
|
|
457
|
|
458 /**
|
|
459 * Replaces the given annotations in this model and if advised fires a
|
|
460 * model change event.
|
|
461 *
|
|
462 * @param annotationsToRemove the annotations to be removed
|
|
463 * @param annotationsToAdd the annotations to be added
|
|
464 * @param fireModelChanged <code>true</code> if a model change event
|
|
465 * should be fired, <code>false</code> otherwise
|
|
466 * @throws BadLocationException in case an annotation should be added at an
|
|
467 * invalid position
|
|
468 * @since 3.0
|
|
469 */
|
|
470 protected void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd, bool fireModelChanged) throws BadLocationException {
|
|
471
|
|
472 if (annotationsToRemove !is null) {
|
|
473 for (int i= 0, length= annotationsToRemove.length; i < length; i++)
|
|
474 removeAnnotation(annotationsToRemove[i], false);
|
|
475 }
|
|
476
|
|
477 if (annotationsToAdd !is null) {
|
|
478 Iterator iter= annotationsToAdd.entrySet().iterator();
|
|
479 while (iter.hasNext()) {
|
|
480 Map.Entry mapEntry= (Map.Entry) iter.next();
|
|
481 Annotation annotation= (Annotation) mapEntry.getKey();
|
|
482 Position position= (Position) mapEntry.getValue();
|
|
483 addAnnotation(annotation, position, false);
|
|
484 }
|
|
485 }
|
|
486
|
|
487 if (fireModelChanged)
|
|
488 fireModelChanged();
|
|
489 }
|
|
490
|
|
491 /**
|
|
492 * Adds the given annotation to this model. Associates the
|
|
493 * annotation with the given position. If requested, all annotation
|
|
494 * model listeners are informed about this model change. If the annotation
|
|
495 * is already managed by this model nothing happens.
|
|
496 *
|
|
497 * @param annotation the annotation to add
|
|
498 * @param position the associate position
|
|
499 * @param fireModelChanged indicates whether to notify all model listeners
|
|
500 * @throws BadLocationException if the position is not a valid document position
|
|
501 */
|
|
502 protected void addAnnotation(Annotation annotation, Position position, bool fireModelChanged) throws BadLocationException {
|
|
503 if (!fAnnotations.containsKey(annotation)) {
|
|
504
|
|
505 addPosition(fDocument, position);
|
|
506 fAnnotations.put(annotation, position);
|
|
507 fPositions.put(position, annotation);
|
|
508 synchronized (getLockObject()) {
|
|
509 getAnnotationModelEvent().annotationAdded(annotation);
|
|
510 }
|
|
511
|
|
512 if (fireModelChanged)
|
|
513 fireModelChanged();
|
|
514 }
|
|
515 }
|
|
516
|
|
517 /*
|
|
518 * @see dwtx.jface.text.source.IAnnotationModel#addAnnotationModelListener(dwtx.jface.text.source.IAnnotationModelListener)
|
|
519 */
|
|
520 public void addAnnotationModelListener(IAnnotationModelListener listener) {
|
|
521 if (!fAnnotationModelListeners.contains(listener)) {
|
|
522 fAnnotationModelListeners.add(listener);
|
|
523 if (listener instanceof IAnnotationModelListenerExtension) {
|
|
524 IAnnotationModelListenerExtension extension= (IAnnotationModelListenerExtension) listener;
|
|
525 AnnotationModelEvent event= createAnnotationModelEvent();
|
|
526 event.markSealed();
|
|
527 extension.modelChanged(event);
|
|
528 } else
|
|
529 listener.modelChanged(this);
|
|
530 }
|
|
531 }
|
|
532
|
|
533 /**
|
|
534 * Adds the given position to the default position category of the
|
|
535 * given document.
|
|
536 *
|
|
537 * @param document the document to which to add the position
|
|
538 * @param position the position to add
|
|
539 * @throws BadLocationException if the position is not a valid document position
|
|
540 */
|
|
541 protected void addPosition(IDocument document, Position position) throws BadLocationException {
|
|
542 if (document !is null)
|
|
543 document.addPosition(position);
|
|
544 }
|
|
545
|
|
546 /**
|
|
547 * Removes the given position from the default position category of the
|
|
548 * given document.
|
|
549 *
|
|
550 * @param document the document to which to add the position
|
|
551 * @param position the position to add
|
|
552 *
|
|
553 * @since 3.0
|
|
554 */
|
|
555 protected void removePosition(IDocument document, Position position) {
|
|
556 if (document !is null)
|
|
557 document.removePosition(position);
|
|
558 }
|
|
559
|
|
560 /*
|
|
561 * @see dwtx.jface.text.source.IAnnotationModel#connect(dwtx.jface.text.IDocument)
|
|
562 */
|
|
563 public void connect(IDocument document) {
|
|
564 Assert.isTrue(fDocument is null || fDocument is document);
|
|
565
|
|
566 if (fDocument is null) {
|
|
567 fDocument= document;
|
|
568 Iterator e= getAnnotationMap().valuesIterator();
|
|
569 while (e.hasNext())
|
|
570 try {
|
|
571 addPosition(fDocument, (Position) e.next());
|
|
572 } catch (BadLocationException x) {
|
|
573 // ignore invalid position
|
|
574 }
|
|
575 }
|
|
576
|
|
577 ++ fOpenConnections;
|
|
578 if (fOpenConnections is 1) {
|
|
579 fDocument.addDocumentListener(fDocumentListener);
|
|
580 connected();
|
|
581 }
|
|
582
|
|
583 for (Iterator it= fAttachments.keySet().iterator(); it.hasNext();) {
|
|
584 IAnnotationModel model= (IAnnotationModel) fAttachments.get(it.next());
|
|
585 model.connect(document);
|
|
586 }
|
|
587 }
|
|
588
|
|
589 /**
|
|
590 * Hook method. Is called as soon as this model becomes connected to a document.
|
|
591 * Subclasses may re-implement.
|
|
592 */
|
|
593 protected void connected() {
|
|
594 }
|
|
595
|
|
596 /**
|
|
597 * Hook method. Is called as soon as this model becomes disconnected from its document.
|
|
598 * Subclasses may re-implement.
|
|
599 */
|
|
600 protected void disconnected() {
|
|
601 }
|
|
602
|
|
603 /*
|
|
604 * @see dwtx.jface.text.source.IAnnotationModel#disconnect(dwtx.jface.text.IDocument)
|
|
605 */
|
|
606 public void disconnect(IDocument document) {
|
|
607
|
|
608 Assert.isTrue(fDocument is document);
|
|
609
|
|
610 for (Iterator it= fAttachments.keySet().iterator(); it.hasNext();) {
|
|
611 IAnnotationModel model= (IAnnotationModel) fAttachments.get(it.next());
|
|
612 model.disconnect(document);
|
|
613 }
|
|
614
|
|
615 -- fOpenConnections;
|
|
616 if (fOpenConnections is 0) {
|
|
617
|
|
618 disconnected();
|
|
619 fDocument.removeDocumentListener(fDocumentListener);
|
|
620
|
|
621 if (fDocument !is null) {
|
|
622 Iterator e= getAnnotationMap().valuesIterator();
|
|
623 while (e.hasNext()) {
|
|
624 Position p= (Position) e.next();
|
|
625 removePosition(fDocument, p);
|
|
626 }
|
|
627 fDocument= null;
|
|
628 }
|
|
629 }
|
|
630 }
|
|
631
|
|
632 /**
|
|
633 * Informs all annotation model listeners that this model has been changed.
|
|
634 */
|
|
635 protected void fireModelChanged() {
|
|
636 AnnotationModelEvent modelEvent= null;
|
|
637
|
|
638 synchronized(getLockObject()) {
|
|
639 if (fModelEvent !is null) {
|
|
640 modelEvent= fModelEvent;
|
|
641 fModelEvent= null;
|
|
642 }
|
|
643 }
|
|
644
|
|
645 if (modelEvent !is null)
|
|
646 fireModelChanged(modelEvent);
|
|
647 }
|
|
648
|
|
649 /**
|
|
650 * Creates and returns a new annotation model event. Subclasses may override.
|
|
651 *
|
|
652 * @return a new and empty annotation model event
|
|
653 * @since 3.0
|
|
654 */
|
|
655 protected AnnotationModelEvent createAnnotationModelEvent() {
|
|
656 return new AnnotationModelEvent(this);
|
|
657 }
|
|
658
|
|
659 /**
|
|
660 * Informs all annotation model listeners that this model has been changed
|
|
661 * as described in the annotation model event. The event is sent out
|
|
662 * to all listeners implementing <code>IAnnotationModelListenerExtension</code>.
|
|
663 * All other listeners are notified by just calling <code>modelChanged(IAnnotationModel)</code>.
|
|
664 *
|
|
665 * @param event the event to be sent out to the listeners
|
|
666 * @since 2.0
|
|
667 */
|
|
668 protected void fireModelChanged(AnnotationModelEvent event) {
|
|
669
|
|
670 event.markSealed();
|
|
671
|
|
672 if (event.isEmpty())
|
|
673 return;
|
|
674
|
|
675 ArrayList v= new ArrayList(fAnnotationModelListeners);
|
|
676 Iterator e= v.iterator();
|
|
677 while (e.hasNext()) {
|
|
678 IAnnotationModelListener l= (IAnnotationModelListener) e.next();
|
|
679 if (l instanceof IAnnotationModelListenerExtension)
|
|
680 ((IAnnotationModelListenerExtension) l).modelChanged(event);
|
|
681 else if (l !is null)
|
|
682 l.modelChanged(this);
|
|
683 }
|
|
684 }
|
|
685
|
|
686 /**
|
|
687 * Removes the given annotations from this model. If requested all
|
|
688 * annotation model listeners will be informed about this change.
|
|
689 * <code>modelInitiated</code> indicates whether the deletion has
|
|
690 * been initiated by this model or by one of its clients.
|
|
691 *
|
|
692 * @param annotations the annotations to be removed
|
|
693 * @param fireModelChanged indicates whether to notify all model listeners
|
|
694 * @param modelInitiated indicates whether this changes has been initiated by this model
|
|
695 */
|
|
696 protected void removeAnnotations(List annotations, bool fireModelChanged, bool modelInitiated) {
|
|
697 if (annotations.size() > 0) {
|
|
698 Iterator e= annotations.iterator();
|
|
699 while (e.hasNext())
|
|
700 removeAnnotation((Annotation) e.next(), false);
|
|
701
|
|
702 if (fireModelChanged)
|
|
703 fireModelChanged();
|
|
704 }
|
|
705 }
|
|
706
|
|
707 /**
|
|
708 * Removes all annotations from the model whose associated positions have been
|
|
709 * deleted. If requested inform all model listeners about the change.
|
|
710 *
|
|
711 * @param fireModelChanged indicates whether to notify all model listeners
|
|
712 */
|
|
713 protected void cleanup(bool fireModelChanged) {
|
|
714 cleanup(fireModelChanged, true);
|
|
715 }
|
|
716
|
|
717 /**
|
|
718 * Removes all annotations from the model whose associated positions have been
|
|
719 * deleted. If requested inform all model listeners about the change. If requested
|
|
720 * a new thread is created for the notification of the model listeners.
|
|
721 *
|
|
722 * @param fireModelChanged indicates whether to notify all model listeners
|
|
723 * @param forkNotification <code>true</code> iff notification should be done in a new thread
|
|
724 * @since 3.0
|
|
725 */
|
|
726 private void cleanup(bool fireModelChanged, bool forkNotification) {
|
|
727 if (fDocumentChanged) {
|
|
728 fDocumentChanged= false;
|
|
729
|
|
730 ArrayList deleted= new ArrayList();
|
|
731 Iterator e= getAnnotationMap().keySetIterator();
|
|
732 while (e.hasNext()) {
|
|
733 Annotation a= (Annotation) e.next();
|
|
734 Position p= (Position) fAnnotations.get(a);
|
|
735 if (p is null || p.isDeleted())
|
|
736 deleted.add(a);
|
|
737 }
|
|
738
|
|
739 if (fireModelChanged && forkNotification) {
|
|
740 removeAnnotations(deleted, false, false);
|
|
741 synchronized (getLockObject()) {
|
|
742 if (fModelEvent !is null)
|
|
743 new Thread() {
|
|
744 public void run() {
|
|
745 fireModelChanged();
|
|
746 }
|
|
747 }.start();
|
|
748 }
|
|
749 } else
|
|
750 removeAnnotations(deleted, fireModelChanged, false);
|
|
751 }
|
|
752 }
|
|
753
|
|
754 /*
|
|
755 * @see dwtx.jface.text.source.IAnnotationModel#getAnnotationIterator()
|
|
756 */
|
|
757 public Iterator getAnnotationIterator() {
|
|
758 return getAnnotationIterator(true, true);
|
|
759 }
|
|
760
|
|
761 /**
|
|
762 * {@inheritDoc}
|
|
763 *
|
|
764 * @since 3.4
|
|
765 */
|
|
766 public Iterator getAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter) {
|
|
767 Iterator regionIterator= getRegionAnnotationIterator(offset, length, canStartBefore, canEndAfter);
|
|
768
|
|
769 if (fAttachments.isEmpty())
|
|
770 return regionIterator;
|
|
771
|
|
772 List iterators= new ArrayList(fAttachments.size() + 1);
|
|
773 iterators.add(regionIterator);
|
|
774 Iterator it= fAttachments.keySet().iterator();
|
|
775 while (it.hasNext()) {
|
|
776 IAnnotationModel attachment= (IAnnotationModel) fAttachments.get(it.next());
|
|
777 if (attachment instanceof IAnnotationModelExtension2)
|
|
778 iterators.add(((IAnnotationModelExtension2) attachment).getAnnotationIterator(offset, length, canStartBefore, canEndAfter));
|
|
779 else
|
|
780 iterators.add(new RegionIterator(attachment.getAnnotationIterator(), attachment, offset, length, canStartBefore, canEndAfter));
|
|
781 }
|
|
782
|
|
783 return new MetaIterator(iterators.iterator());
|
|
784 }
|
|
785
|
|
786 /**
|
|
787 * Returns an iterator as specified in {@link IAnnotationModelExtension2#getAnnotationIterator(int, int, bool, bool)}
|
|
788 *
|
|
789 * @param offset region start
|
|
790 * @param length region length
|
|
791 * @param canStartBefore position can start before region
|
|
792 * @param canEndAfter position can end after region
|
|
793 * @return an iterator to iterate over annotations in region
|
|
794 * @see IAnnotationModelExtension2#getAnnotationIterator(int, int, bool, bool)
|
|
795 * @since 3.4
|
|
796 */
|
|
797 private Iterator getRegionAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter) {
|
|
798 if (!(fDocument instanceof AbstractDocument))
|
|
799 return new RegionIterator(getAnnotationIterator(true), this, offset, length, canStartBefore, canEndAfter);
|
|
800
|
|
801 AbstractDocument document= (AbstractDocument) fDocument;
|
|
802 cleanup(true);
|
|
803
|
|
804 try {
|
|
805 Position[] positions= document.getPositions(IDocument.DEFAULT_CATEGORY, offset, length, canStartBefore, canEndAfter);
|
|
806 return new AnnotationsInterator(positions, fPositions);
|
|
807 } catch (BadPositionCategoryException e) {
|
|
808 //can not happen
|
|
809 Assert.isTrue(false);
|
|
810 return null;
|
|
811 }
|
|
812 }
|
|
813
|
|
814 /**
|
|
815 * Returns all annotations managed by this model. <code>cleanup</code>
|
|
816 * indicates whether all annotations whose associated positions are
|
|
817 * deleted should previously be removed from the model. <code>recurse</code> indicates
|
|
818 * whether annotations of attached sub-models should also be returned.
|
|
819 *
|
|
820 * @param cleanup indicates whether annotations with deleted associated positions are removed
|
|
821 * @param recurse whether to return annotations managed by sub-models.
|
|
822 * @return all annotations managed by this model
|
|
823 * @since 3.0
|
|
824 */
|
|
825 private Iterator getAnnotationIterator(bool cleanup, bool recurse) {
|
|
826 Iterator iter= getAnnotationIterator(cleanup);
|
|
827 if (!recurse || fAttachments.isEmpty())
|
|
828 return iter;
|
|
829
|
|
830 List iterators= new ArrayList(fAttachments.size() + 1);
|
|
831 iterators.add(iter);
|
|
832 Iterator it= fAttachments.keySet().iterator();
|
|
833 while (it.hasNext())
|
|
834 iterators.add(((IAnnotationModel) fAttachments.get(it.next())).getAnnotationIterator());
|
|
835
|
|
836 return new MetaIterator(iterators.iterator());
|
|
837 }
|
|
838
|
|
839 /**
|
|
840 * Returns all annotations managed by this model. <code>cleanup</code>
|
|
841 * indicates whether all annotations whose associated positions are
|
|
842 * deleted should previously be removed from the model.
|
|
843 *
|
|
844 * @param cleanup indicates whether annotations with deleted associated positions are removed
|
|
845 * @return all annotations managed by this model
|
|
846 */
|
|
847 protected Iterator getAnnotationIterator(bool cleanup) {
|
|
848 if (cleanup)
|
|
849 cleanup(true);
|
|
850
|
|
851 return getAnnotationMap().keySetIterator();
|
|
852 }
|
|
853
|
|
854 /*
|
|
855 * @see dwtx.jface.text.source.IAnnotationModel#getPosition(dwtx.jface.text.source.Annotation)
|
|
856 */
|
|
857 public Position getPosition(Annotation annotation) {
|
|
858 Position position= (Position) fAnnotations.get(annotation);
|
|
859 if (position !is null)
|
|
860 return position;
|
|
861
|
|
862 Iterator it= fAttachments.values().iterator();
|
|
863 while (position is null && it.hasNext())
|
|
864 position= ((IAnnotationModel) it.next()).getPosition(annotation);
|
|
865 return position;
|
|
866 }
|
|
867
|
|
868 /*
|
|
869 * @see dwtx.jface.text.source.IAnnotationModelExtension#removeAllAnnotations()
|
|
870 * @since 3.0
|
|
871 */
|
|
872 public void removeAllAnnotations() {
|
|
873 removeAllAnnotations(true);
|
|
874 }
|
|
875
|
|
876 /**
|
|
877 * Removes all annotations from the annotation model. If requested
|
|
878 * inform all model change listeners about this change.
|
|
879 *
|
|
880 * @param fireModelChanged indicates whether to notify all model listeners
|
|
881 */
|
|
882 protected void removeAllAnnotations(bool fireModelChanged) {
|
|
883
|
|
884 if (fDocument !is null) {
|
|
885 Iterator e= getAnnotationMap().keySetIterator();
|
|
886 while (e.hasNext()) {
|
|
887 Annotation a= (Annotation) e.next();
|
|
888 Position p= (Position) fAnnotations.get(a);
|
|
889 removePosition(fDocument, p);
|
|
890 // p.delete();
|
|
891 synchronized (getLockObject()) {
|
|
892 getAnnotationModelEvent().annotationRemoved(a, p);
|
|
893 }
|
|
894 }
|
|
895 }
|
|
896
|
|
897 fAnnotations.clear();
|
|
898 fPositions.clear();
|
|
899
|
|
900 if (fireModelChanged)
|
|
901 fireModelChanged();
|
|
902 }
|
|
903
|
|
904 /*
|
|
905 * @see dwtx.jface.text.source.IAnnotationModel#removeAnnotation(dwtx.jface.text.source.Annotation)
|
|
906 */
|
|
907 public void removeAnnotation(Annotation annotation) {
|
|
908 removeAnnotation(annotation, true);
|
|
909 }
|
|
910
|
|
911 /**
|
|
912 * Removes the given annotation from the annotation model.
|
|
913 * If requested inform all model change listeners about this change.
|
|
914 *
|
|
915 * @param annotation the annotation to be removed
|
|
916 * @param fireModelChanged indicates whether to notify all model listeners
|
|
917 */
|
|
918 protected void removeAnnotation(Annotation annotation, bool fireModelChanged) {
|
|
919 if (fAnnotations.containsKey(annotation)) {
|
|
920
|
|
921 Position p= null;
|
|
922 p= (Position) fAnnotations.get(annotation);
|
|
923 if (fDocument !is null) {
|
|
924 removePosition(fDocument, p);
|
|
925 // p.delete();
|
|
926 }
|
|
927
|
|
928 fAnnotations.remove(annotation);
|
|
929 fPositions.remove(p);
|
|
930 synchronized (getLockObject()) {
|
|
931 getAnnotationModelEvent().annotationRemoved(annotation, p);
|
|
932 }
|
|
933
|
|
934 if (fireModelChanged)
|
|
935 fireModelChanged();
|
|
936 }
|
|
937 }
|
|
938
|
|
939 /*
|
|
940 * @see dwtx.jface.text.source.IAnnotationModelExtension#modifyAnnotationPosition(dwtx.jface.text.source.Annotation, dwtx.jface.text.Position)
|
|
941 * @since 3.0
|
|
942 */
|
|
943 public void modifyAnnotationPosition(Annotation annotation, Position position) {
|
|
944 modifyAnnotationPosition(annotation, position, true);
|
|
945 }
|
|
946
|
|
947 /**
|
|
948 * Modifies the associated position of the given annotation to the given
|
|
949 * position. If the annotation is not yet managed by this annotation model,
|
|
950 * the annotation is added. When the position is <code>null</code>, the
|
|
951 * annotation is removed from the model.
|
|
952 * <p>
|
|
953 * If requested, all annotation model change listeners will be informed
|
|
954 * about the change.
|
|
955 *
|
|
956 * @param annotation the annotation whose associated position should be
|
|
957 * modified
|
|
958 * @param position the position to whose values the associated position
|
|
959 * should be changed
|
|
960 * @param fireModelChanged indicates whether to notify all model listeners
|
|
961 * @since 3.0
|
|
962 */
|
|
963 protected void modifyAnnotationPosition(Annotation annotation, Position position, bool fireModelChanged) {
|
|
964 if (position is null) {
|
|
965 removeAnnotation(annotation, fireModelChanged);
|
|
966 } else {
|
|
967 Position p= (Position) fAnnotations.get(annotation);
|
|
968 if (p !is null) {
|
|
969
|
|
970 if (position.getOffset() !is p.getOffset() || position.getLength() !is p.getLength()) {
|
|
971 fDocument.removePosition(p);
|
|
972 p.setOffset(position.getOffset());
|
|
973 p.setLength(position.getLength());
|
|
974 try {
|
|
975 fDocument.addPosition(p);
|
|
976 } catch (BadLocationException e) {
|
|
977 // ignore invalid position
|
|
978 }
|
|
979 }
|
|
980 synchronized (getLockObject()) {
|
|
981 getAnnotationModelEvent().annotationChanged(annotation);
|
|
982 }
|
|
983 if (fireModelChanged)
|
|
984 fireModelChanged();
|
|
985
|
|
986 } else {
|
|
987 try {
|
|
988 addAnnotation(annotation, position, fireModelChanged);
|
|
989 } catch (BadLocationException x) {
|
|
990 // ignore invalid position
|
|
991 }
|
|
992 }
|
|
993 }
|
|
994 }
|
|
995
|
|
996 /**
|
|
997 * Modifies the given annotation if the annotation is managed by this
|
|
998 * annotation model.
|
|
999 * <p>
|
|
1000 * If requested, all annotation model change listeners will be informed
|
|
1001 * about the change.
|
|
1002 *
|
|
1003 * @param annotation the annotation to be modified
|
|
1004 * @param fireModelChanged indicates whether to notify all model listeners
|
|
1005 * @since 3.0
|
|
1006 */
|
|
1007 protected void modifyAnnotation(Annotation annotation, bool fireModelChanged) {
|
|
1008 if (fAnnotations.containsKey(annotation)) {
|
|
1009 synchronized (getLockObject()) {
|
|
1010 getAnnotationModelEvent().annotationChanged(annotation);
|
|
1011 }
|
|
1012 if (fireModelChanged)
|
|
1013 fireModelChanged();
|
|
1014 }
|
|
1015 }
|
|
1016
|
|
1017 /*
|
|
1018 * @see IAnnotationModel#removeAnnotationModelListener(IAnnotationModelListener)
|
|
1019 */
|
|
1020 public void removeAnnotationModelListener(IAnnotationModelListener listener) {
|
|
1021 fAnnotationModelListeners.remove(listener);
|
|
1022 }
|
|
1023
|
|
1024 /*
|
|
1025 * @see dwtx.jface.text.source.IAnnotationModelExtension#attach(java.lang.Object, java.lang.Object)
|
|
1026 * @since 3.0
|
|
1027 */
|
|
1028 public void addAnnotationModel(Object key, IAnnotationModel attachment) {
|
|
1029 Assert.isNotNull(attachment);
|
|
1030 if (!fAttachments.containsValue(attachment)) {
|
|
1031 fAttachments.put(key, attachment);
|
|
1032 for (int i= 0; i < fOpenConnections; i++)
|
|
1033 attachment.connect(fDocument);
|
|
1034 attachment.addAnnotationModelListener(fModelListener);
|
|
1035 }
|
|
1036 }
|
|
1037
|
|
1038 /*
|
|
1039 * @see dwtx.jface.text.source.IAnnotationModelExtension#get(java.lang.Object)
|
|
1040 * @since 3.0
|
|
1041 */
|
|
1042 public IAnnotationModel getAnnotationModel(Object key) {
|
|
1043 return (IAnnotationModel) fAttachments.get(key);
|
|
1044 }
|
|
1045
|
|
1046 /*
|
|
1047 * @see dwtx.jface.text.source.IAnnotationModelExtension#detach(java.lang.Object)
|
|
1048 * @since 3.0
|
|
1049 */
|
|
1050 public IAnnotationModel removeAnnotationModel(Object key) {
|
|
1051 IAnnotationModel ret= (IAnnotationModel) fAttachments.remove(key);
|
|
1052 if (ret !is null) {
|
|
1053 for (int i= 0; i < fOpenConnections; i++)
|
|
1054 ret.disconnect(fDocument);
|
|
1055 ret.removeAnnotationModelListener(fModelListener);
|
|
1056 }
|
|
1057 return ret;
|
|
1058 }
|
|
1059
|
|
1060 /*
|
|
1061 * @see dwtx.jface.text.source.IAnnotationModelExtension#getModificationStamp()
|
|
1062 * @since 3.0
|
|
1063 */
|
|
1064 public Object getModificationStamp() {
|
|
1065 return fModificationStamp;
|
|
1066 }
|
|
1067 }
|