Mercurial > projects > dwt-addons
annotate dwtx/jface/text/source/AnnotationModel.d @ 135:65801ad2b265
Regex fix for anon classes
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 24 Aug 2008 01:52:31 +0200 |
parents | 51e6e63f930e |
children | 6dcb0baaa031 |
rev | line source |
---|---|
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 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
141 public this(Iterator parentIterator, IAnnotationModel model, int offset, int length, bool canStartBefore, bool canEndAfter) { |
129 | 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()) { | |
134 | 178 Annotation next= cast(Annotation) fParentIterator.next(); |
129 | 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 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
218 public this(Position[] positions, Map map) { |
129 | 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 | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
275 public this(Iterator iterator) { |
129 | 276 fSuperIterator= iterator; |
134 | 277 fCurrent= cast(Iterator) fSuperIterator.next(); // there is at least one. |
129 | 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()) { | |
134 | 292 fCurrent= cast(Iterator) fSuperIterator.next(); |
129 | 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 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
376 public this() { |
129 | 377 fAnnotations= new AnnotationMap(10); |
378 fPositions= new IdentityHashMap(10); | |
379 fAnnotationModelListeners= new ArrayList(2); | |
380 | |
135 | 381 fDocumentListener= new class() IDocumentListener { |
129 | 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() { | |
134 | 399 return cast(IAnnotationMap) fAnnotations; |
129 | 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(); | |
134 | 481 Annotation annotation= cast(Annotation) mapEntry.getKey(); |
482 Position position= cast(Position) mapEntry.getValue(); | |
129 | 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) { | |
134 | 524 IAnnotationModelListenerExtension extension= cast(IAnnotationModelListenerExtension) listener; |
129 | 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 { | |
134 | 571 addPosition(fDocument, cast(Position) e.next()); |
129 | 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();) { | |
134 | 584 IAnnotationModel model= cast(IAnnotationModel) fAttachments.get(it.next()); |
129 | 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();) { | |
134 | 611 IAnnotationModel model= cast(IAnnotationModel) fAttachments.get(it.next()); |
129 | 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()) { | |
134 | 624 Position p= cast(Position) e.next(); |
129 | 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()) { | |
134 | 678 IAnnotationModelListener l= cast(IAnnotationModelListener) e.next(); |
129 | 679 if (l instanceof IAnnotationModelListenerExtension) |
134 | 680 (cast(IAnnotationModelListenerExtension) l).modelChanged(event); |
129 | 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()) | |
134 | 700 removeAnnotation(cast(Annotation) e.next(), false); |
129 | 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()) { | |
134 | 733 Annotation a= cast(Annotation) e.next(); |
734 Position p= cast(Position) fAnnotations.get(a); | |
129 | 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) | |
135 | 743 new class() Thread { |
129 | 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()) { | |
134 | 776 IAnnotationModel attachment= cast(IAnnotationModel) fAttachments.get(it.next()); |
129 | 777 if (attachment instanceof IAnnotationModelExtension2) |
134 | 778 iterators.add((cast(IAnnotationModelExtension2) attachment).getAnnotationIterator(offset, length, canStartBefore, canEndAfter)); |
129 | 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 | |
134 | 801 AbstractDocument document= cast(AbstractDocument) fDocument; |
129 | 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()) | |
134 | 834 iterators.add((cast(IAnnotationModel) fAttachments.get(it.next())).getAnnotationIterator()); |
129 | 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) { | |
134 | 858 Position position= cast(Position) fAnnotations.get(annotation); |
129 | 859 if (position !is null) |
860 return position; | |
861 | |
862 Iterator it= fAttachments.values().iterator(); | |
863 while (position is null && it.hasNext()) | |
134 | 864 position= (cast(IAnnotationModel) it.next()).getPosition(annotation); |
129 | 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()) { | |
134 | 887 Annotation a= cast(Annotation) e.next(); |
888 Position p= cast(Position) fAnnotations.get(a); | |
129 | 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; | |
134 | 922 p= cast(Position) fAnnotations.get(annotation); |
129 | 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 { | |
134 | 967 Position p= cast(Position) fAnnotations.get(annotation); |
129 | 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) { | |
134 | 1043 return cast(IAnnotationModel) fAttachments.get(key); |
129 | 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) { | |
134 | 1051 IAnnotationModel ret= cast(IAnnotationModel) fAttachments.remove(key); |
129 | 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 } |