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