comparison org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.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
14
15 module org.eclipse.jface.text.AbstractDocument;
16
17 import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
18 import org.eclipse.jface.text.DefaultTextHover; // packageimport
19 import org.eclipse.jface.text.AbstractInformationControl; // packageimport
20 import org.eclipse.jface.text.TextUtilities; // packageimport
21 import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
22 import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
23 import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
24 import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
25 import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
26 import org.eclipse.jface.text.ITextSelection; // packageimport
27 import org.eclipse.jface.text.Document; // packageimport
28 import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
29 import org.eclipse.jface.text.ITextListener; // packageimport
30 import org.eclipse.jface.text.BadPartitioningException; // packageimport
31 import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
32 import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
33 import org.eclipse.jface.text.IUndoManager; // packageimport
34 import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
35 import org.eclipse.jface.text.IRepairableDocument; // packageimport
36 import org.eclipse.jface.text.IRewriteTarget; // packageimport
37 import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
38 import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
39 import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
40 import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
41 import org.eclipse.jface.text.TextViewer; // packageimport
42 import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
43 import org.eclipse.jface.text.RegExMessages; // packageimport
44 import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
45 import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
46 import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
47 import org.eclipse.jface.text.IViewportListener; // packageimport
48 import org.eclipse.jface.text.GapTextStore; // packageimport
49 import org.eclipse.jface.text.MarkSelection; // packageimport
50 import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
51 import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
52 import org.eclipse.jface.text.IInformationControlExtension; // packageimport
53 import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
54 import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
55 import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
56 import org.eclipse.jface.text.IInformationControlCreator; // packageimport
57 import org.eclipse.jface.text.TypedRegion; // packageimport
58 import org.eclipse.jface.text.ISynchronizable; // packageimport
59 import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
60 import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
61 import org.eclipse.jface.text.IRegion; // packageimport
62 import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
63 import org.eclipse.jface.text.IDocumentExtension4; // packageimport
64 import org.eclipse.jface.text.IDocumentExtension2; // packageimport
65 import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
66 import org.eclipse.jface.text.DefaultInformationControl; // packageimport
67 import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
68 import org.eclipse.jface.text.DocumentClone; // packageimport
69 import org.eclipse.jface.text.DefaultUndoManager; // packageimport
70 import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
71 import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
72 import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
73 import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
74 import org.eclipse.jface.text.TextSelection; // packageimport
75 import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
76 import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
77 import org.eclipse.jface.text.IPainter; // packageimport
78 import org.eclipse.jface.text.IInformationControl; // packageimport
79 import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
80 import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
81 import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
82 import org.eclipse.jface.text.DefaultLineTracker; // packageimport
83 import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
84 import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
85 import org.eclipse.jface.text.ITextHover; // packageimport
86 import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
87 import org.eclipse.jface.text.ILineTracker; // packageimport
88 import org.eclipse.jface.text.Line; // packageimport
89 import org.eclipse.jface.text.ITextViewerExtension; // packageimport
90 import org.eclipse.jface.text.IDocumentAdapter; // packageimport
91 import org.eclipse.jface.text.TextEvent; // packageimport
92 import org.eclipse.jface.text.BadLocationException; // packageimport
93 import org.eclipse.jface.text.AbstractLineTracker; // packageimport
94 import org.eclipse.jface.text.TreeLineTracker; // packageimport
95 import org.eclipse.jface.text.ITextPresentationListener; // packageimport
96 import org.eclipse.jface.text.Region; // packageimport
97 import org.eclipse.jface.text.ITextViewer; // packageimport
98 import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
99 import org.eclipse.jface.text.MarginPainter; // packageimport
100 import org.eclipse.jface.text.IPaintPositionManager; // packageimport
101 import org.eclipse.jface.text.TextPresentation; // packageimport
102 import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
103 import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
104 import org.eclipse.jface.text.ISelectionValidator; // packageimport
105 import org.eclipse.jface.text.IDocumentExtension; // packageimport
106 import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
107 import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
108 import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
109 import org.eclipse.jface.text.IDocumentListener; // packageimport
110 import org.eclipse.jface.text.PaintManager; // packageimport
111 import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
112 import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
113 import org.eclipse.jface.text.IDocumentExtension3; // packageimport
114 import org.eclipse.jface.text.Position; // packageimport
115 import org.eclipse.jface.text.TextMessages; // packageimport
116 import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
117 import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
118 import org.eclipse.jface.text.IPositionUpdater; // packageimport
119 import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
120 import org.eclipse.jface.text.ListLineTracker; // packageimport
121 import org.eclipse.jface.text.ITextInputListener; // packageimport
122 import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
123 import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
124 import org.eclipse.jface.text.IInputChangedListener; // packageimport
125 import org.eclipse.jface.text.ITextOperationTarget; // packageimport
126 import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
127 import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
128 import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
129 import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
130 import org.eclipse.jface.text.JFaceTextUtil; // packageimport
131 import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
132 import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
133 import org.eclipse.jface.text.CursorLinePainter; // packageimport
134 import org.eclipse.jface.text.ITextHoverExtension; // packageimport
135 import org.eclipse.jface.text.IEventConsumer; // packageimport
136 import org.eclipse.jface.text.IDocument; // packageimport
137 import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
138 import org.eclipse.jface.text.DocumentCommand; // packageimport
139 import org.eclipse.jface.text.TypedPosition; // packageimport
140 import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
141 import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
142 import org.eclipse.jface.text.AbstractHoverInformationControlManager; // packageimport
143 import org.eclipse.jface.text.IEditingSupport; // packageimport
144 import org.eclipse.jface.text.IMarkSelection; // packageimport
145 import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
146 import org.eclipse.jface.text.DocumentEvent; // packageimport
147 import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
148 import org.eclipse.jface.text.ITextStore; // packageimport
149 import org.eclipse.jface.text.JFaceTextMessages; // packageimport
150 import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
151 import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
152 import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
153 import org.eclipse.jface.text.TextAttribute; // packageimport
154 import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
155 import org.eclipse.jface.text.ITypedRegion; // packageimport
156
157 import java.lang.all;
158 import java.util.Arrays;
159 import java.util.List;
160 import java.util.ArrayList;
161 import java.util.Iterator;
162 import java.util.Map;
163 import java.util.HashMap;
164 import java.util.Set;
165 import org.eclipse.dwtxhelper.regex;
166 import tango.text.convert.Format;
167
168 import org.eclipse.core.runtime.Assert;
169 import org.eclipse.core.runtime.ListenerList;
170
171
172 /**
173 * Abstract default implementation of <code>IDocument</code> and its extension
174 * interfaces {@link org.eclipse.jface.text.IDocumentExtension},
175 * {@link org.eclipse.jface.text.IDocumentExtension2},
176 * {@link org.eclipse.jface.text.IDocumentExtension3},
177 * {@link org.eclipse.jface.text.IDocumentExtension4}, as well as
178 * {@link org.eclipse.jface.text.IRepairableDocument}.
179 * <p>
180 *
181 * An <code>AbstractDocument</code> supports the following implementation
182 * plug-ins:
183 * <ul>
184 * <li>a text store implementing {@link org.eclipse.jface.text.ITextStore} for
185 * storing and managing the document's content,</li>
186 * <li>a line tracker implementing {@link org.eclipse.jface.text.ILineTracker}
187 * to map character positions to line numbers and vice versa</li>
188 * </ul>
189 * The document can dynamically change the text store when switching between
190 * sequential rewrite mode and normal mode.
191 * <p>
192 *
193 * This class must be subclassed. Subclasses must configure which implementation
194 * plug-ins the document instance should use. Subclasses are not intended to
195 * overwrite existing methods.
196 *
197 * @see org.eclipse.jface.text.ITextStore
198 * @see org.eclipse.jface.text.ILineTracker
199 */
200 public abstract class AbstractDocument : IDocument, IDocumentExtension, IDocumentExtension2, IDocumentExtension3, IDocumentExtension4, IRepairableDocument, IRepairableDocumentExtension {
201
202 /**
203 * Tells whether this class is in debug mode.
204 * @since 3.1
205 */
206 private static const bool DEBUG= false;
207
208
209 /**
210 * Inner class to bundle a registered post notification replace operation together with its
211 * owner.
212 *
213 * @since 2.0
214 */
215 static private class RegisteredReplace {
216 /** The owner of this replace operation. */
217 IDocumentListener fOwner;
218 /** The replace operation */
219 IDocumentExtension.IReplace fReplace;
220
221 /**
222 * Creates a new bundle object.
223 * @param owner the document listener owning the replace operation
224 * @param replace the replace operation
225 */
226 this(IDocumentListener owner, IDocumentExtension.IReplace replace) {
227 fOwner= owner;
228 fReplace= replace;
229 }
230 }
231
232
233 /** The document's text store */
234 private ITextStore fStore;
235 /** The document's line tracker */
236 private ILineTracker fTracker;
237 /** The registered document listeners */
238 private ListenerList fDocumentListeners;
239 /** The registered pre-notified document listeners */
240 private ListenerList fPrenotifiedDocumentListeners;
241 /** The registered document partitioning listeners */
242 private ListenerList fDocumentPartitioningListeners;
243 /** All positions managed by the document ordered by their start positions. */
244 private Map fPositions;
245 /**
246 * All positions managed by the document ordered by there end positions.
247 * @since 3.4
248 */
249 private Map fEndPositions;
250 /** All registered document position updaters */
251 private List fPositionUpdaters;
252 /**
253 * The list of post notification changes
254 * @since 2.0
255 */
256 private List fPostNotificationChanges;
257 /**
258 * The reentrance count for post notification changes.
259 * @since 2.0
260 */
261 private int fReentranceCount= 0;
262 /**
263 * Indicates whether post notification change processing has been stopped.
264 * @since 2.0
265 */
266 private int fStoppedCount= 0;
267 /**
268 * Indicates whether the registration of post notification changes should be ignored.
269 * @since 2.1
270 */
271 private bool fAcceptPostNotificationReplaces= true;
272 /**
273 * Indicates whether the notification of listeners has been stopped.
274 * @since 2.1
275 */
276 private int fStoppedListenerNotification= 0;
277 /**
278 * The document event to be sent after listener notification has been resumed.
279 * @since 2.1
280 */
281 private DocumentEvent fDeferredDocumentEvent;
282 /**
283 * The registered document partitioners.
284 * @since 3.0
285 */
286 private Map fDocumentPartitioners;
287 /**
288 * The partitioning changed event.
289 * @since 3.0
290 */
291 private DocumentPartitioningChangedEvent fDocumentPartitioningChangedEvent;
292 /**
293 * The find/replace document adapter.
294 * @since 3.0
295 */
296 private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
297 /**
298 * The active document rewrite session.
299 * @since 3.1
300 */
301 private DocumentRewriteSession fDocumentRewriteSession;
302 /**
303 * The registered document rewrite session listeners.
304 * @since 3.1
305 */
306 private List fDocumentRewriteSessionListeners;
307 /**
308 * The current modification stamp.
309 * @since 3.1
310 */
311 private long fModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
312 /**
313 * Keeps track of next modification stamp.
314 * @since 3.1.1
315 */
316 private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
317 /**
318 * This document's default line delimiter.
319 * @since 3.1
320 */
321 private String fInitialLineDelimiter;
322
323
324 /**
325 * The default constructor does not perform any configuration
326 * but leaves it to the clients who must first initialize the
327 * implementation plug-ins and then call <code>completeInitialization</code>.
328 * Results in the construction of an empty document.
329 */
330 protected this() {
331 fModificationStamp= getNextModificationStamp();
332 }
333
334
335 /**
336 * Returns the document's text store. Assumes that the
337 * document has been initialized with a text store.
338 *
339 * @return the document's text store
340 */
341 protected ITextStore getStore() {
342 Assert.isNotNull(cast(Object)fStore);
343 return fStore;
344 }
345
346 /**
347 * Returns the document's line tracker. Assumes that the
348 * document has been initialized with a line tracker.
349 *
350 * @return the document's line tracker
351 */
352 protected ILineTracker getTracker() {
353 Assert.isNotNull(cast(Object)fTracker);
354 return fTracker;
355 }
356
357 /**
358 * Returns the document's document listeners.
359 *
360 * @return the document's document listeners
361 */
362 protected List getDocumentListeners() {
363 return Arrays.asList(fDocumentListeners.getListeners());
364 }
365
366 /**
367 * Returns the document's partitioning listeners.
368 *
369 * @return the document's partitioning listeners
370 */
371 protected List getDocumentPartitioningListeners() {
372 return Arrays.asList(fDocumentPartitioningListeners.getListeners());
373 }
374
375 /**
376 * Returns all positions managed by the document grouped by category.
377 *
378 * @return the document's positions
379 */
380 protected Map getDocumentManagedPositions() {
381 return fPositions;
382 }
383
384 /*
385 * @see org.eclipse.jface.text.IDocument#getDocumentPartitioner()
386 */
387 public IDocumentPartitioner getDocumentPartitioner() {
388 return getDocumentPartitioner(DEFAULT_PARTITIONING);
389 }
390
391
392
393 //--- implementation configuration interface ------------
394
395 /**
396 * Sets the document's text store.
397 * Must be called at the beginning of the constructor.
398 *
399 * @param store the document's text store
400 */
401 protected void setTextStore(ITextStore store) {
402 fStore= store;
403 }
404
405 /**
406 * Sets the document's line tracker.
407 * Must be called at the beginning of the constructor.
408 *
409 * @param tracker the document's line tracker
410 */
411 protected void setLineTracker(ILineTracker tracker) {
412 fTracker= tracker;
413 }
414
415 /*
416 * @see org.eclipse.jface.text.IDocument#setDocumentPartitioner(org.eclipse.jface.text.IDocumentPartitioner)
417 */
418 public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
419 setDocumentPartitioner(DEFAULT_PARTITIONING, partitioner);
420 }
421
422 /**
423 * Initializes document listeners, positions, and position updaters.
424 * Must be called inside the constructor after the implementation plug-ins
425 * have been set.
426 */
427 protected void completeInitialization() {
428
429 fPositions= new HashMap();
430 fEndPositions= new HashMap();
431 fPositionUpdaters= new ArrayList();
432 fDocumentListeners= new ListenerList(ListenerList.IDENTITY);
433 fPrenotifiedDocumentListeners= new ListenerList(ListenerList.IDENTITY);
434 fDocumentPartitioningListeners= new ListenerList(ListenerList.IDENTITY);
435 fDocumentRewriteSessionListeners= new ArrayList();
436
437 addPositionCategory(DEFAULT_CATEGORY);
438 addPositionUpdater(new DefaultPositionUpdater(DEFAULT_CATEGORY));
439 }
440
441
442 //-------------------------------------------------------
443
444 /*
445 * @see org.eclipse.jface.text.IDocument#addDocumentListener(org.eclipse.jface.text.IDocumentListener)
446 */
447 public void addDocumentListener(IDocumentListener listener) {
448 Assert.isNotNull(cast(Object)listener);
449 fDocumentListeners.add(cast(Object)listener);
450 }
451
452 /*
453 * @see org.eclipse.jface.text.IDocument#removeDocumentListener(org.eclipse.jface.text.IDocumentListener)
454 */
455 public void removeDocumentListener(IDocumentListener listener) {
456 Assert.isNotNull(cast(Object)listener);
457 fDocumentListeners.remove(cast(Object)listener);
458 }
459
460 /*
461 * @see org.eclipse.jface.text.IDocument#addPrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
462 */
463 public void addPrenotifiedDocumentListener(IDocumentListener listener) {
464 Assert.isNotNull(cast(Object)listener);
465 fPrenotifiedDocumentListeners.add(cast(Object)listener);
466 }
467
468 /*
469 * @see org.eclipse.jface.text.IDocument#removePrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
470 */
471 public void removePrenotifiedDocumentListener(IDocumentListener listener) {
472 Assert.isNotNull(cast(Object)listener);
473 fPrenotifiedDocumentListeners.remove(cast(Object)listener);
474 }
475
476 /*
477 * @see org.eclipse.jface.text.IDocument#addDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
478 */
479 public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
480 Assert.isNotNull(cast(Object)listener);
481 fDocumentPartitioningListeners.add(cast(Object)listener);
482 }
483
484 /*
485 * @see org.eclipse.jface.text.IDocument#removeDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
486 */
487 public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
488 Assert.isNotNull(cast(Object)listener);
489 fDocumentPartitioningListeners.remove(cast(Object)listener);
490 }
491
492 /*
493 * @see org.eclipse.jface.text.IDocument#addPosition(java.lang.String, org.eclipse.jface.text.Position)
494 */
495 public void addPosition(String category, Position position) {
496
497 if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength()))
498 throw new BadLocationException();
499
500 if (category is null)
501 throw new BadPositionCategoryException();
502
503 List list= cast(List) fPositions.get(category);
504 if (list is null)
505 throw new BadPositionCategoryException();
506 list.add(computeIndexInPositionList(list, position.offset), position);
507
508 List endPositions= cast(List) fEndPositions.get(category);
509 if (endPositions is null)
510 throw new BadPositionCategoryException();
511 endPositions.add(computeIndexInPositionList(endPositions, position.offset + position.length - 1, false), position);
512 }
513
514 /*
515 * @see org.eclipse.jface.text.IDocument#addPosition(org.eclipse.jface.text.Position)
516 */
517 public void addPosition(Position position) {
518 try {
519 addPosition(DEFAULT_CATEGORY, position);
520 } catch (BadPositionCategoryException e) {
521 }
522 }
523
524 /*
525 * @see org.eclipse.jface.text.IDocument#addPositionCategory(java.lang.String)
526 */
527 public void addPositionCategory(String category) {
528
529 if (category is null)
530 return;
531
532 if (!containsPositionCategory(category)) {
533 fPositions.put(category, new ArrayList());
534 fEndPositions.put(category, new ArrayList());
535 }
536 }
537
538 /*
539 * @see org.eclipse.jface.text.IDocument#addPositionUpdater(org.eclipse.jface.text.IPositionUpdater)
540 */
541 public void addPositionUpdater(IPositionUpdater updater) {
542 insertPositionUpdater(updater, fPositionUpdaters.size());
543 }
544
545 /*
546 * @see org.eclipse.jface.text.IDocument#containsPosition(java.lang.String, int, int)
547 */
548 public bool containsPosition(String category, int offset, int length) {
549
550 if (category is null)
551 return false;
552
553 List list= cast(List) fPositions.get(category);
554 if (list is null)
555 return false;
556
557 int size= list.size();
558 if (size is 0)
559 return false;
560
561 int index= computeIndexInPositionList(list, offset);
562 if (index < size) {
563 Position p= cast(Position) list.get(index);
564 while (p !is null && p.offset is offset) {
565 if (p.length is length)
566 return true;
567 ++ index;
568 p= (index < size) ? cast(Position) list.get(index) : null;
569 }
570 }
571
572 return false;
573 }
574
575 /*
576 * @see org.eclipse.jface.text.IDocument#containsPositionCategory(java.lang.String)
577 */
578 public bool containsPositionCategory(String category) {
579 if (category !is null)
580 return fPositions.containsKey(category);
581 return false;
582 }
583
584
585 /**
586 * Computes the index in the list of positions at which a position with the given
587 * offset would be inserted. The position is supposed to become the first in this list
588 * of all positions with the same offset.
589 *
590 * @param positions the list in which the index is computed
591 * @param offset the offset for which the index is computed
592 * @return the computed index
593 *
594 * @see IDocument#computeIndexInCategory(String, int)
595 * @deprecated As of 3.4, replaced by {@link #computeIndexInPositionList(List, int, bool)}
596 */
597 protected int computeIndexInPositionList(List positions, int offset) {
598 return computeIndexInPositionList(positions, offset, true);
599 }
600
601 /**
602 * Computes the index in the list of positions at which a position with the given
603 * position would be inserted. The position to insert is supposed to become the first
604 * in this list of all positions with the same position.
605 *
606 * @param positions the list in which the index is computed
607 * @param offset the offset for which the index is computed
608 * @param orderedByOffset <code>true</code> if ordered by offset, false if ordered by end position
609 * @return the computed index
610 * @since 3.4
611 */
612 protected int computeIndexInPositionList(List positions, int offset, bool orderedByOffset) {
613 if (positions.size() is 0)
614 return 0;
615
616 int left= 0;
617 int right= positions.size() -1;
618 int mid= 0;
619 Position p= null;
620
621 while (left < right) {
622
623 mid= (left + right) / 2;
624
625 p= cast(Position) positions.get(mid);
626 int pOffset= getOffset(orderedByOffset, p);
627 if (offset < pOffset) {
628 if (left is mid)
629 right= left;
630 else
631 right= mid -1;
632 } else if (offset > pOffset) {
633 if (right is mid)
634 left= right;
635 else
636 left= mid +1;
637 } else if (offset is pOffset) {
638 left= right= mid;
639 }
640
641 }
642
643 int pos= left;
644 p= cast(Position) positions.get(pos);
645 int pPosition= getOffset(orderedByOffset, p);
646 if (offset > pPosition) {
647 // append to the end
648 pos++;
649 } else {
650 // entry will become the first of all entries with the same offset
651 do {
652 --pos;
653 if (pos < 0)
654 break;
655 p= cast(Position) positions.get(pos);
656 pPosition= getOffset(orderedByOffset, p);
657 } while (offset is pPosition);
658 ++pos;
659 }
660
661 Assert.isTrue(0 <= pos && pos <= positions.size());
662
663 return pos;
664 }
665
666 /*
667 * @since 3.4
668 */
669 private int getOffset(bool orderedByOffset, Position position) {
670 if (orderedByOffset || position.getLength() is 0)
671 return position.getOffset();
672 return position.getOffset() + position.getLength() - 1;
673 }
674
675 /*
676 * @see org.eclipse.jface.text.IDocument#computeIndexInCategory(java.lang.String, int)
677 */
678 public int computeIndexInCategory(String category, int offset) {
679
680 if (0 > offset || offset > getLength())
681 throw new BadLocationException();
682
683 List c= cast(List) fPositions.get(category);
684 if (c is null)
685 throw new BadPositionCategoryException();
686
687 return computeIndexInPositionList(c, offset);
688 }
689
690 /**
691 * Fires the document partitioning changed notification to all registered
692 * document partitioning listeners. Uses a robust iterator.
693 *
694 * @deprecated as of 2.0. Use <code>fireDocumentPartitioningChanged(IRegion)</code> instead.
695 */
696 protected void fireDocumentPartitioningChanged() {
697 if (fDocumentPartitioningListeners is null)
698 return;
699
700 Object[] listeners= fDocumentPartitioningListeners.getListeners();
701 for (int i= 0; i < listeners.length; i++)
702 (cast(IDocumentPartitioningListener)listeners[i]).documentPartitioningChanged(this);
703 }
704
705 /**
706 * Fires the document partitioning changed notification to all registered
707 * document partitioning listeners. Uses a robust iterator.
708 *
709 * @param region the region in which partitioning has changed
710 *
711 * @see IDocumentPartitioningListenerExtension
712 * @since 2.0
713 * @deprecated as of 3.0. Use
714 * <code>fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent)</code>
715 * instead.
716 */
717 protected void fireDocumentPartitioningChanged(IRegion region) {
718 if (fDocumentPartitioningListeners is null)
719 return;
720
721 Object[] listeners= fDocumentPartitioningListeners.getListeners();
722 for (int i= 0; i < listeners.length; i++) {
723 IDocumentPartitioningListener l= cast(IDocumentPartitioningListener)listeners[i];
724 if ( cast(IDocumentPartitioningListenerExtension)l )
725 (cast(IDocumentPartitioningListenerExtension) l).documentPartitioningChanged(this, region);
726 else
727 l.documentPartitioningChanged(this);
728 }
729 }
730
731 /**
732 * Fires the document partitioning changed notification to all registered
733 * document partitioning listeners. Uses a robust iterator.
734 *
735 * @param event the document partitioning changed event
736 *
737 * @see IDocumentPartitioningListenerExtension2
738 * @since 3.0
739 */
740 protected void fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent event) {
741 if (fDocumentPartitioningListeners is null)
742 return;
743
744 Object[] listeners= fDocumentPartitioningListeners.getListeners();
745 for (int i= 0; i < listeners.length; i++) {
746 IDocumentPartitioningListener l= cast(IDocumentPartitioningListener)listeners[i];
747 if ( cast(IDocumentPartitioningListenerExtension2)l ) {
748 IDocumentPartitioningListenerExtension2 extension2= cast(IDocumentPartitioningListenerExtension2) l;
749 extension2.documentPartitioningChanged(event);
750 } else if ( cast(IDocumentPartitioningListenerExtension)l ) {
751 IDocumentPartitioningListenerExtension extension= cast(IDocumentPartitioningListenerExtension) l;
752 extension.documentPartitioningChanged(this, event.getCoverage());
753 } else {
754 l.documentPartitioningChanged(this);
755 }
756 }
757 }
758
759 /**
760 * Fires the given document event to all registers document listeners informing them
761 * about the forthcoming document manipulation. Uses a robust iterator.
762 *
763 * @param event the event to be sent out
764 */
765 protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
766
767 // IDocumentExtension
768 if (fReentranceCount is 0)
769 flushPostNotificationChanges();
770
771 if (fDocumentPartitioners !is null) {
772 Iterator e= fDocumentPartitioners.values().iterator();
773 while (e.hasNext()) {
774 IDocumentPartitioner p= cast(IDocumentPartitioner) e.next();
775 if ( cast(IDocumentPartitionerExtension3)p ) {
776 IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) p;
777 if (extension.getActiveRewriteSession() !is null)
778 continue;
779 }
780 p.documentAboutToBeChanged(event);
781 }
782 }
783
784 Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
785 for (int i= 0; i < listeners.length; i++)
786 (cast(IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
787
788 listeners= fDocumentListeners.getListeners();
789 for (int i= 0; i < listeners.length; i++)
790 (cast(IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
791 }
792
793
794 /**
795 * Updates document partitioning and document positions according to the
796 * specification given by the document event.
797 *
798 * @param event the document event describing the change to which structures must be adapted
799 */
800 protected void updateDocumentStructures(DocumentEvent event) {
801
802 if (fDocumentPartitioners !is null) {
803 fDocumentPartitioningChangedEvent= new DocumentPartitioningChangedEvent(this);
804 Iterator e= fDocumentPartitioners.keySet().iterator();
805 while (e.hasNext()) {
806 String partitioning= stringcast( e.next() );
807 IDocumentPartitioner partitioner= cast(IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
808
809 if ( cast(IDocumentPartitionerExtension3)partitioner ) {
810 IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
811 if (extension.getActiveRewriteSession() !is null)
812 continue;
813 }
814
815 if ( cast(IDocumentPartitionerExtension)partitioner ) {
816 IDocumentPartitionerExtension extension= cast(IDocumentPartitionerExtension) partitioner;
817 IRegion r= extension.documentChanged2(event);
818 if (r !is null)
819 fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, r.getOffset(), r.getLength());
820 } else {
821 if (partitioner.documentChanged(event))
822 fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, 0, event.getDocument().getLength());
823 }
824 }
825 }
826
827 if (fPositions.size() > 0)
828 updatePositions(event);
829 }
830
831 /**
832 * Notifies all listeners about the given document change. Uses a robust
833 * iterator.
834 * <p>
835 * Executes all registered post notification replace operation.
836 *
837 * @param event the event to be sent out.
838 */
839 protected void doFireDocumentChanged(DocumentEvent event) {
840 bool changed= fDocumentPartitioningChangedEvent !is null && !fDocumentPartitioningChangedEvent.isEmpty();
841 IRegion change= changed ? fDocumentPartitioningChangedEvent.getCoverage() : null;
842 doFireDocumentChanged(event, changed, change);
843 }
844
845 /**
846 * Notifies all listeners about the given document change.
847 * Uses a robust iterator. <p>
848 * Executes all registered post notification replace operation.
849 *
850 * @param event the event to be sent out
851 * @param firePartitionChange <code>true</code> if a partition change notification should be sent
852 * @param partitionChange the region whose partitioning changed
853 * @since 2.0
854 * @deprecated as of 3.0. Use <code>doFireDocumentChanged2(DocumentEvent)</code> instead; this method will be removed.
855 */
856 protected void doFireDocumentChanged(DocumentEvent event, bool firePartitionChange, IRegion partitionChange) {
857 doFireDocumentChanged2(event);
858 }
859
860 /**
861 * Notifies all listeners about the given document change. Uses a robust
862 * iterator.
863 * <p>
864 * Executes all registered post notification replace operation.
865 * <p>
866 * This method will be renamed to <code>doFireDocumentChanged</code>.
867 *
868 * @param event the event to be sent out
869 * @since 3.0
870 */
871 protected void doFireDocumentChanged2(DocumentEvent event) {
872
873 DocumentPartitioningChangedEvent p= fDocumentPartitioningChangedEvent;
874 fDocumentPartitioningChangedEvent= null;
875 if (p !is null && !p.isEmpty())
876 fireDocumentPartitioningChanged(p);
877
878 Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
879 for (int i= 0; i < listeners.length; i++)
880 (cast(IDocumentListener)listeners[i]).documentChanged(event);
881
882 listeners= fDocumentListeners.getListeners();
883 for (int i= 0; i < listeners.length; i++)
884 (cast(IDocumentListener)listeners[i]).documentChanged(event);
885
886 // IDocumentExtension
887 ++ fReentranceCount;
888 try {
889 if (fReentranceCount is 1)
890 executePostNotificationChanges();
891 } finally {
892 -- fReentranceCount;
893 }
894 }
895
896 /**
897 * Updates the internal document structures and informs all document listeners
898 * if listener notification has been enabled. Otherwise it remembers the event
899 * to be sent to the listeners on resume.
900 *
901 * @param event the document event to be sent out
902 */
903 protected void fireDocumentChanged(DocumentEvent event) {
904 updateDocumentStructures(event);
905
906 if (fStoppedListenerNotification is 0)
907 doFireDocumentChanged(event);
908 else
909 fDeferredDocumentEvent= event;
910 }
911
912 /*
913 * @see org.eclipse.jface.text.IDocument#getChar(int)
914 */
915 public char getChar(int pos) {
916 if ((0 > pos) || (pos >= getLength()))
917 throw new BadLocationException();
918 return getStore().get(pos);
919 }
920
921 /*
922 * @see org.eclipse.jface.text.IDocument#getContentType(int)
923 */
924 public String getContentType(int offset) {
925 String contentType= null;
926 try {
927 contentType= getContentType(DEFAULT_PARTITIONING, offset, false);
928 //Assert.isNotNull(contentType);
929 } catch (BadPartitioningException e) {
930 Assert.isTrue(false);
931 }
932 return contentType;
933 }
934
935 /*
936 * @see org.eclipse.jface.text.IDocument#getLegalContentTypes()
937 */
938 public String[] getLegalContentTypes() {
939 String[] contentTypes= null;
940 try {
941 contentTypes= getLegalContentTypes(DEFAULT_PARTITIONING);
942 //Assert.isNotNull(contentTypes);
943 } catch (BadPartitioningException e) {
944 Assert.isTrue(false);
945 }
946 return contentTypes;
947 }
948
949 /*
950 * @see org.eclipse.jface.text.IDocument#getLength()
951 */
952 public int getLength() {
953 return getStore().getLength();
954 }
955
956 /*
957 * @see org.eclipse.jface.text.IDocument#getLineDelimiter(int)
958 */
959 public String getLineDelimiter(int line) {
960 return getTracker().getLineDelimiter(line);
961 }
962
963 /*
964 * @see org.eclipse.jface.text.IDocument#getLegalLineDelimiters()
965 */
966 public String[] getLegalLineDelimiters() {
967 return getTracker().getLegalLineDelimiters();
968 }
969
970 /*
971 * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
972 * @since 3.1
973 */
974 public String getDefaultLineDelimiter() {
975
976 String lineDelimiter= null;
977
978 try {
979 lineDelimiter= getLineDelimiter(0);
980 } catch (BadLocationException x) {
981 }
982
983 if (lineDelimiter !is null)
984 return lineDelimiter;
985
986 if (fInitialLineDelimiter !is null)
987 return fInitialLineDelimiter;
988
989 String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
990 String[] delimiters= getLegalLineDelimiters();
991 Assert.isTrue(delimiters.length > 0);
992 for (int i= 0; i < delimiters.length; i++) {
993 if (delimiters[i].equals(sysLineDelimiter)) {
994 lineDelimiter= sysLineDelimiter;
995 break;
996 }
997 }
998
999 if (lineDelimiter is null)
1000 lineDelimiter= delimiters[0];
1001
1002 return lineDelimiter;
1003
1004 }
1005
1006 /*
1007 * @see org.eclipse.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String)
1008 * @since 3.1
1009 */
1010 public void setInitialLineDelimiter(String lineDelimiter) {
1011 Assert.isNotNull(lineDelimiter);
1012 fInitialLineDelimiter= lineDelimiter;
1013 }
1014
1015 /*
1016 * @see org.eclipse.jface.text.IDocument#getLineLength(int)
1017 */
1018 public int getLineLength(int line) {
1019 return getTracker().getLineLength(line);
1020 }
1021
1022 /*
1023 * @see org.eclipse.jface.text.IDocument#getLineOfOffset(int)
1024 */
1025 public int getLineOfOffset(int pos) {
1026 return getTracker().getLineNumberOfOffset(pos);
1027 }
1028
1029 /*
1030 * @see org.eclipse.jface.text.IDocument#getLineOffset(int)
1031 */
1032 public int getLineOffset(int line) {
1033 return getTracker().getLineOffset(line);
1034 }
1035
1036 /*
1037 * @see org.eclipse.jface.text.IDocument#getLineInformation(int)
1038 */
1039 public IRegion getLineInformation(int line) {
1040 return getTracker().getLineInformation(line);
1041 }
1042
1043 /*
1044 * @see org.eclipse.jface.text.IDocument#getLineInformationOfOffset(int)
1045 */
1046 public IRegion getLineInformationOfOffset(int offset) {
1047 return getTracker().getLineInformationOfOffset(offset);
1048 }
1049
1050 /*
1051 * @see org.eclipse.jface.text.IDocument#getNumberOfLines()
1052 */
1053 public int getNumberOfLines() {
1054 return getTracker().getNumberOfLines();
1055 }
1056
1057 /*
1058 * @see org.eclipse.jface.text.IDocument#getNumberOfLines(int, int)
1059 */
1060 public int getNumberOfLines(int offset, int length) {
1061 return getTracker().getNumberOfLines(offset, length);
1062 }
1063
1064 /*
1065 * @see org.eclipse.jface.text.IDocument#computeNumberOfLines(java.lang.String)
1066 */
1067 public int computeNumberOfLines(String text) {
1068 return getTracker().computeNumberOfLines(text);
1069 }
1070
1071 /*
1072 * @see org.eclipse.jface.text.IDocument#getPartition(int)
1073 */
1074 public ITypedRegion getPartition(int offset) {
1075 ITypedRegion partition= null;
1076 try {
1077 partition= getPartition(DEFAULT_PARTITIONING, offset, false);
1078 // Assert.isNotNull(cast(Object)partition);
1079 } catch (BadPartitioningException e) {
1080 Assert.isTrue(false);
1081 }
1082 return partition;
1083 }
1084
1085 /*
1086 * @see org.eclipse.jface.text.IDocument#computePartitioning(int, int)
1087 */
1088 public ITypedRegion[] computePartitioning(int offset, int length) {
1089 ITypedRegion[] partitioning= null;
1090 try {
1091 partitioning= computePartitioning(DEFAULT_PARTITIONING, offset, length, false);
1092 // Assert.isNotNull(cast(Object)partitioning);
1093 } catch (BadPartitioningException e) {
1094 Assert.isTrue(false);
1095 }
1096 return partitioning;
1097 }
1098
1099 /*
1100 * @see org.eclipse.jface.text.IDocument#getPositions(java.lang.String)
1101 */
1102 public Position[] getPositions(String category) {
1103
1104 if (category is null)
1105 throw new BadPositionCategoryException();
1106
1107 List c= cast(List) fPositions.get(category);
1108 if (c is null)
1109 throw new BadPositionCategoryException();
1110
1111 Position[] positions= new Position[c.size()];
1112 c.toArray(positions);
1113 return positions;
1114 }
1115
1116 /*
1117 * @see org.eclipse.jface.text.IDocument#getPositionCategories()
1118 */
1119 public String[] getPositionCategories() {
1120 String[] categories= new String[fPositions.size()];
1121 Iterator keys= fPositions.keySet().iterator();
1122 for (int i= 0; i < categories.length; i++)
1123 categories[i]= stringcast( keys.next());
1124 return categories;
1125 }
1126
1127 /*
1128 * @see org.eclipse.jface.text.IDocument#getPositionUpdaters()
1129 */
1130 public IPositionUpdater[] getPositionUpdaters() {
1131 return arraycast!(IPositionUpdater)(fPositionUpdaters.toArray());
1132 }
1133
1134 /*
1135 * @see org.eclipse.jface.text.IDocument#get()
1136 */
1137 public String get() {
1138 return getStore().get(0, getLength());
1139 }
1140
1141 /*
1142 * @see org.eclipse.jface.text.IDocument#get(int, int)
1143 */
1144 public String get(int pos, int length) {
1145 int myLength= getLength();
1146 if ((0 > pos) || (0 > length) || (pos + length > myLength))
1147 throw new BadLocationException();
1148 return getStore().get(pos, length);
1149 }
1150
1151 /*
1152 * @see org.eclipse.jface.text.IDocument#insertPositionUpdater(org.eclipse.jface.text.IPositionUpdater, int)
1153 */
1154 public void insertPositionUpdater(IPositionUpdater updater, int index) {
1155
1156 for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
1157 if (fPositionUpdaters.get(i) is cast(Object)updater)
1158 return;
1159 }
1160
1161 if (index is fPositionUpdaters.size())
1162 fPositionUpdaters.add(cast(Object)updater);
1163 else
1164 fPositionUpdaters.add(index, cast(Object)updater);
1165 }
1166
1167 /*
1168 * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, org.eclipse.jface.text.Position)
1169 */
1170 public void removePosition(String category, Position position) {
1171
1172 if (position is null)
1173 return;
1174
1175 if (category is null)
1176 throw new BadPositionCategoryException();
1177
1178 List c= cast(List) fPositions.get(category);
1179 if (c is null)
1180 throw new BadPositionCategoryException();
1181 removeFromPositionsList(c, position, true);
1182
1183 List endPositions= cast(List) fEndPositions.get(category);
1184 if (endPositions is null)
1185 throw new BadPositionCategoryException();
1186 removeFromPositionsList(endPositions, position, false);
1187 }
1188
1189 /**
1190 * Remove the given position form the given list of positions based on identity not equality.
1191 *
1192 * @param positions a list of positions
1193 * @param position the position to remove
1194 * @param orderedByOffset true if <code>positions</code> is ordered by offset, false if ordered by end position
1195 * @since 3.4
1196 */
1197 private void removeFromPositionsList(List positions, Position position, bool orderedByOffset) {
1198 int size= positions.size();
1199
1200 //Assume position is somewhere near it was before
1201 int index= computeIndexInPositionList(positions, orderedByOffset ? position.offset : position.offset + position.length - 1, orderedByOffset);
1202 if (index < size && positions.get(index) is position) {
1203 positions.remove(index);
1204 return;
1205 }
1206
1207 int back= index - 1;
1208 int forth= index + 1;
1209 while (back >= 0 || forth < size) {
1210 if (back >= 0) {
1211 if (position is positions.get(back)) {
1212 positions.remove(back);
1213 return;
1214 }
1215 back--;
1216 }
1217
1218 if (forth < size) {
1219 if (position is positions.get(forth)) {
1220 positions.remove(forth);
1221 return;
1222 }
1223 forth++;
1224 }
1225 }
1226 }
1227
1228 /*
1229 * @see org.eclipse.jface.text.IDocument#removePosition(org.eclipse.jface.text.Position)
1230 */
1231 public void removePosition(Position position) {
1232 try {
1233 removePosition(DEFAULT_CATEGORY, position);
1234 } catch (BadPositionCategoryException e) {
1235 }
1236 }
1237
1238 /*
1239 * @see org.eclipse.jface.text.IDocument#removePositionCategory(java.lang.String)
1240 */
1241 public void removePositionCategory(String category) {
1242
1243 if (category is null)
1244 return;
1245
1246 if ( !containsPositionCategory(category))
1247 throw new BadPositionCategoryException();
1248
1249 fPositions.remove(category);
1250 fEndPositions.remove(category);
1251 }
1252
1253 /*
1254 * @see org.eclipse.jface.text.IDocument#removePositionUpdater(org.eclipse.jface.text.IPositionUpdater)
1255 */
1256 public void removePositionUpdater(IPositionUpdater updater) {
1257 for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
1258 if (fPositionUpdaters.get(i) is cast(Object)updater) {
1259 fPositionUpdaters.remove(i);
1260 return;
1261 }
1262 }
1263 }
1264
1265 private long getNextModificationStamp() {
1266 if (fNextModificationStamp is Long.MAX_VALUE || fNextModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
1267 fNextModificationStamp= 0;
1268 else
1269 fNextModificationStamp= fNextModificationStamp + 1;
1270
1271 return fNextModificationStamp;
1272 }
1273
1274 /*
1275 * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp()
1276 * @since 3.1
1277 */
1278 public long getModificationStamp() {
1279 return fModificationStamp;
1280 }
1281
1282 /*
1283 * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
1284 * @since 3.1
1285 */
1286 public void replace(int pos, int length, String text, long modificationStamp) {
1287 if ((0 > pos) || (0 > length) || (pos + length > getLength()))
1288 throw new BadLocationException();
1289
1290 DocumentEvent e= new DocumentEvent(this, pos, length, text);
1291 fireDocumentAboutToBeChanged(e);
1292
1293 getStore().replace(pos, length, text);
1294 getTracker().replace(pos, length, text);
1295
1296 fModificationStamp= modificationStamp;
1297 fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
1298 e.fModificationStamp= fModificationStamp;
1299
1300 fireDocumentChanged(e);
1301 }
1302
1303 /**
1304 * {@inheritDoc}
1305 *
1306 * @since 3.4
1307 */
1308 public bool isLineInformationRepairNeeded(int offset, int length, String text) {
1309 return false;
1310 }
1311
1312 /*
1313 * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
1314 */
1315 public void replace(int pos, int length, String text) {
1316 if (length is 0 && (text is null || text.length is 0))
1317 replace(pos, length, text, getModificationStamp());
1318 else
1319 replace(pos, length, text, getNextModificationStamp());
1320 }
1321
1322 /*
1323 * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
1324 */
1325 public void set(String text) {
1326 set(text, getNextModificationStamp());
1327 }
1328
1329 /*
1330 * @see org.eclipse.jface.text.IDocumentExtension4#set(java.lang.String, long)
1331 * @since 3.1
1332 */
1333 public void set(String text, long modificationStamp) {
1334 int length= getStore().getLength();
1335
1336 DocumentEvent e= new DocumentEvent(this, 0, length, text);
1337 fireDocumentAboutToBeChanged(e);
1338
1339 getStore().set(text);
1340 getTracker().set(text);
1341
1342 fModificationStamp= modificationStamp;
1343 fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
1344 e.fModificationStamp= fModificationStamp;
1345
1346 fireDocumentChanged(e);
1347 }
1348
1349 /**
1350 * Updates all positions of all categories to the change described by the
1351 * document event. All registered document updaters are called in the
1352 * sequence they have been arranged. Uses a robust iterator.
1353 *
1354 * @param event the document event describing the change to which to adapt
1355 * the positions
1356 */
1357 protected void updatePositions(DocumentEvent event) {
1358 List list= new ArrayList(fPositionUpdaters);
1359 Iterator e= list.iterator();
1360 while (e.hasNext()) {
1361 IPositionUpdater u= cast(IPositionUpdater) e.next();
1362 u.update(event);
1363 }
1364 }
1365
1366 /*
1367 * @see org.eclipse.jface.text.IDocument#search(int, java.lang.String, bool, bool, bool)
1368 */
1369 public int search(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord) {
1370 try {
1371 IRegion region= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
1372 return region is null ? -1 : region.getOffset();
1373 } catch (IllegalStateException ex) {
1374 return -1;
1375 } catch (PatternSyntaxException ex) {
1376 return -1;
1377 }
1378 }
1379
1380 /**
1381 * Returns the find/replace adapter for this document.
1382 *
1383 * @return this document's find/replace document adapter
1384 * @since 3.0
1385 */
1386 private FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
1387 if (fFindReplaceDocumentAdapter is null)
1388 fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(this);
1389
1390 return fFindReplaceDocumentAdapter;
1391 }
1392
1393 /**
1394 * Flushes all registered post notification changes.
1395 *
1396 * @since 2.0
1397 */
1398 private void flushPostNotificationChanges() {
1399 if (fPostNotificationChanges !is null)
1400 fPostNotificationChanges.clear();
1401 }
1402
1403 /**
1404 * Executes all registered post notification changes. The process is
1405 * repeated until no new post notification changes are added.
1406 *
1407 * @since 2.0
1408 */
1409 private void executePostNotificationChanges() {
1410
1411 if (fStoppedCount > 0)
1412 return;
1413
1414 while (fPostNotificationChanges !is null) {
1415 List changes= fPostNotificationChanges;
1416 fPostNotificationChanges= null;
1417
1418 Iterator e= changes.iterator();
1419 while (e.hasNext()) {
1420 RegisteredReplace replace= cast(RegisteredReplace) e.next();
1421 replace.fReplace.perform(this, replace.fOwner);
1422 }
1423 }
1424 }
1425
1426 /*
1427 * @see org.eclipse.jface.text.IDocumentExtension2#acceptPostNotificationReplaces()
1428 * @since 2.1
1429 */
1430 public void acceptPostNotificationReplaces() {
1431 fAcceptPostNotificationReplaces= true;
1432 }
1433
1434 /*
1435 * @see org.eclipse.jface.text.IDocumentExtension2#ignorePostNotificationReplaces()
1436 * @since 2.1
1437 */
1438 public void ignorePostNotificationReplaces() {
1439 fAcceptPostNotificationReplaces= false;
1440 }
1441
1442 /*
1443 * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener, org.eclipse.jface.text.IDocumentExtension.IReplace)
1444 * @since 2.0
1445 */
1446 public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
1447 if (fAcceptPostNotificationReplaces) {
1448 if (fPostNotificationChanges is null)
1449 fPostNotificationChanges= new ArrayList(1);
1450 fPostNotificationChanges.add(new RegisteredReplace(owner, replace));
1451 }
1452 }
1453
1454 /*
1455 * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing()
1456 * @since 2.0
1457 */
1458 public void stopPostNotificationProcessing() {
1459 ++ fStoppedCount;
1460 }
1461
1462 /*
1463 * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing()
1464 * @since 2.0
1465 */
1466 public void resumePostNotificationProcessing() {
1467 -- fStoppedCount;
1468 if (fStoppedCount is 0 && fReentranceCount is 0)
1469 executePostNotificationChanges();
1470 }
1471
1472 /*
1473 * @see org.eclipse.jface.text.IDocumentExtension#startSequentialRewrite(bool)
1474 * @since 2.0
1475 */
1476 public void startSequentialRewrite(bool normalized) {
1477 }
1478
1479 /*
1480 * @see org.eclipse.jface.text.IDocumentExtension#stopSequentialRewrite()
1481 * @since 2.0
1482 */
1483 public void stopSequentialRewrite() {
1484 }
1485
1486 /*
1487 * @see org.eclipse.jface.text.IDocumentExtension2#resumeListenerNotification()
1488 * @since 2.1
1489 */
1490 public void resumeListenerNotification() {
1491 -- fStoppedListenerNotification;
1492 if (fStoppedListenerNotification is 0) {
1493 resumeDocumentListenerNotification();
1494 }
1495 }
1496
1497 /*
1498 * @see org.eclipse.jface.text.IDocumentExtension2#stopListenerNotification()
1499 * @since 2.1
1500 */
1501 public void stopListenerNotification() {
1502 ++ fStoppedListenerNotification;
1503 }
1504
1505 /**
1506 * Resumes the document listener notification by sending out the remembered
1507 * partition changed and document event.
1508 *
1509 * @since 2.1
1510 */
1511 private void resumeDocumentListenerNotification() {
1512 if (fDeferredDocumentEvent !is null) {
1513 DocumentEvent event= fDeferredDocumentEvent;
1514 fDeferredDocumentEvent= null;
1515 doFireDocumentChanged(event);
1516 }
1517 }
1518
1519 /*
1520 * @see org.eclipse.jface.text.IDocumentExtension3#computeZeroLengthPartitioning(java.lang.String, int, int)
1521 * @since 3.0
1522 */
1523 public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, bool includeZeroLengthPartitions) {
1524 if ((0 > offset) || (0 > length) || (offset + length > getLength()))
1525 throw new BadLocationException();
1526
1527 IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
1528
1529 if ( cast(IDocumentPartitionerExtension2)partitioner ) {
1530 checkStateOfPartitioner(partitioner, partitioning);
1531 return (cast(IDocumentPartitionerExtension2) partitioner).computePartitioning(offset, length, includeZeroLengthPartitions);
1532 } else if (partitioner !is null) {
1533 checkStateOfPartitioner(partitioner, partitioning);
1534 return partitioner.computePartitioning(offset, length);
1535 } else if (DEFAULT_PARTITIONING.equals(partitioning))
1536 return [ new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE) ];
1537 else
1538 throw new BadPartitioningException();
1539 }
1540
1541 /*
1542 * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthContentType(java.lang.String, int)
1543 * @since 3.0
1544 */
1545 public String getContentType(String partitioning, int offset, bool preferOpenPartitions) {
1546 if ((0 > offset) || (offset > getLength()))
1547 throw new BadLocationException();
1548
1549 IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
1550
1551 if ( cast(IDocumentPartitionerExtension2)partitioner ) {
1552 checkStateOfPartitioner(partitioner, partitioning);
1553 return (cast(IDocumentPartitionerExtension2) partitioner).getContentType(offset, preferOpenPartitions);
1554 } else if (partitioner !is null) {
1555 checkStateOfPartitioner(partitioner, partitioning);
1556 return partitioner.getContentType(offset);
1557 } else if (DEFAULT_PARTITIONING.equals(partitioning))
1558 return DEFAULT_CONTENT_TYPE;
1559 else
1560 throw new BadPartitioningException();
1561 }
1562
1563 /*
1564 * @see org.eclipse.jface.text.IDocumentExtension3#getDocumentPartitioner(java.lang.String)
1565 * @since 3.0
1566 */
1567 public IDocumentPartitioner getDocumentPartitioner(String partitioning) {
1568 return fDocumentPartitioners !is null ? cast(IDocumentPartitioner) fDocumentPartitioners.get(partitioning) : null;
1569 }
1570
1571 /*
1572 * @see org.eclipse.jface.text.IDocumentExtension3#getLegalContentTypes(java.lang.String)
1573 * @since 3.0
1574 */
1575 public String[] getLegalContentTypes(String partitioning) {
1576 IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
1577 if (partitioner !is null)
1578 return partitioner.getLegalContentTypes();
1579 if (DEFAULT_PARTITIONING.equals(partitioning))
1580 return [ DEFAULT_CONTENT_TYPE ];
1581 throw new BadPartitioningException();
1582 }
1583
1584 /*
1585 * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthPartition(java.lang.String, int)
1586 * @since 3.0
1587 */
1588 public ITypedRegion getPartition(String partitioning, int offset, bool preferOpenPartitions) {
1589 if ((0 > offset) || (offset > getLength()))
1590 throw new BadLocationException();
1591
1592 IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
1593
1594 if ( cast(IDocumentPartitionerExtension2)partitioner ) {
1595 checkStateOfPartitioner(partitioner, partitioning);
1596 return (cast(IDocumentPartitionerExtension2) partitioner).getPartition(offset, preferOpenPartitions);
1597 } else if (partitioner !is null) {
1598 checkStateOfPartitioner(partitioner, partitioning);
1599 return partitioner.getPartition(offset);
1600 } else if (DEFAULT_PARTITIONING.equals(partitioning))
1601 return new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
1602 else
1603 throw new BadPartitioningException();
1604 }
1605
1606 /*
1607 * @see org.eclipse.jface.text.IDocumentExtension3#getPartitionings()
1608 * @since 3.0
1609 */
1610 public String[] getPartitionings() {
1611 if (fDocumentPartitioners is null)
1612 return new String[0];
1613 return stringcast(fDocumentPartitioners.keySet().toArray());
1614 }
1615
1616 /*
1617 * @see org.eclipse.jface.text.IDocumentExtension3#setDocumentPartitioner(java.lang.String, org.eclipse.jface.text.IDocumentPartitioner)
1618 * @since 3.0
1619 */
1620 public void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner) {
1621 if (partitioner is null) {
1622 if (fDocumentPartitioners !is null) {
1623 fDocumentPartitioners.remove(partitioning);
1624 if (fDocumentPartitioners.size() is 0)
1625 fDocumentPartitioners= null;
1626 }
1627 } else {
1628 if (fDocumentPartitioners is null)
1629 fDocumentPartitioners= new HashMap();
1630 fDocumentPartitioners.put(partitioning, cast(Object)partitioner);
1631 }
1632 DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
1633 event.setPartitionChange(partitioning, 0, getLength());
1634 fireDocumentPartitioningChanged(event);
1635 }
1636
1637 /*
1638 * @see org.eclipse.jface.text.IRepairableDocument#repairLineInformation()
1639 * @since 3.0
1640 */
1641 public void repairLineInformation() {
1642 getTracker().set(get());
1643 }
1644
1645 /**
1646 * Fires the given event to all registered rewrite session listeners. Uses robust iterators.
1647 *
1648 * @param event the event to be fired
1649 * @since 3.1
1650 */
1651 protected void fireRewriteSessionChanged(DocumentRewriteSessionEvent event) {
1652 if (fDocumentRewriteSessionListeners.size() > 0) {
1653 List list= new ArrayList(fDocumentRewriteSessionListeners);
1654 Iterator e= list.iterator();
1655 while (e.hasNext()) {
1656 IDocumentRewriteSessionListener l= cast(IDocumentRewriteSessionListener) e.next();
1657 l.documentRewriteSessionChanged(event);
1658 }
1659 }
1660 }
1661
1662 /*
1663 * @see org.eclipse.jface.text.IDocumentExtension4#getActiveRewriteSession()
1664 */
1665 public final DocumentRewriteSession getActiveRewriteSession() {
1666 return fDocumentRewriteSession;
1667 }
1668
1669 /*
1670 * @see org.eclipse.jface.text.IDocumentExtension4#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSessionType)
1671 * @since 3.1
1672 */
1673 public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) {
1674
1675 if (getActiveRewriteSession() !is null)
1676 throw new IllegalStateException();
1677
1678
1679 fDocumentRewriteSession= new DocumentRewriteSession(sessionType);
1680 if (DEBUG)
1681 System.out_.println(Format("AbstractDocument: Starting rewrite session: {}", fDocumentRewriteSession)); //$NON-NLS-1$
1682
1683 fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, fDocumentRewriteSession, DocumentRewriteSessionEvent.SESSION_START));
1684
1685 startRewriteSessionOnPartitioners(fDocumentRewriteSession);
1686
1687 ILineTracker tracker= getTracker();
1688 if ( cast(ILineTrackerExtension)tracker ) {
1689 ILineTrackerExtension extension= cast(ILineTrackerExtension) tracker;
1690 extension.startRewriteSession(fDocumentRewriteSession);
1691 }
1692
1693 if (DocumentRewriteSessionType.SEQUENTIAL is sessionType)
1694 startSequentialRewrite(false);
1695 else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL is sessionType)
1696 startSequentialRewrite(true);
1697
1698 return fDocumentRewriteSession;
1699 }
1700
1701 /**
1702 * Starts the given rewrite session.
1703 *
1704 * @param session the rewrite session
1705 * @since 3.1
1706 */
1707 protected final void startRewriteSessionOnPartitioners(DocumentRewriteSession session) {
1708 if (fDocumentPartitioners !is null) {
1709 Iterator e= fDocumentPartitioners.values().iterator();
1710 while (e.hasNext()) {
1711 Object partitioner= e.next();
1712 if ( cast(IDocumentPartitionerExtension3)partitioner ) {
1713 IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
1714 extension.startRewriteSession(session);
1715 }
1716 }
1717 }
1718 }
1719
1720 /*
1721 * @see org.eclipse.jface.text.IDocumentExtension4#stopRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
1722 * @since 3.1
1723 */
1724 public void stopRewriteSession(DocumentRewriteSession session) {
1725 if (fDocumentRewriteSession is session) {
1726
1727 if (DEBUG)
1728 System.out_.println(Format("AbstractDocument: Stopping rewrite session: {}", session)); //$NON-NLS-1$
1729
1730 DocumentRewriteSessionType sessionType= session.getSessionType();
1731 if (DocumentRewriteSessionType.SEQUENTIAL is sessionType || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL is sessionType)
1732 stopSequentialRewrite();
1733
1734 ILineTracker tracker= getTracker();
1735 if ( cast(ILineTrackerExtension)tracker ) {
1736 ILineTrackerExtension extension= cast(ILineTrackerExtension) tracker;
1737 extension.stopRewriteSession(session, get());
1738 }
1739
1740 stopRewriteSessionOnPartitioners(fDocumentRewriteSession);
1741
1742 fDocumentRewriteSession= null;
1743 fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_STOP));
1744 }
1745 }
1746
1747 /**
1748 * Stops the given rewrite session.
1749 *
1750 * @param session the rewrite session
1751 * @since 3.1
1752 */
1753 protected final void stopRewriteSessionOnPartitioners(DocumentRewriteSession session) {
1754 if (fDocumentPartitioners !is null) {
1755 DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
1756 Iterator e= fDocumentPartitioners.keySet().iterator();
1757 while (e.hasNext()) {
1758 String partitioning= stringcast( e.next());
1759 IDocumentPartitioner partitioner= cast(IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
1760 if ( cast(IDocumentPartitionerExtension3)partitioner ) {
1761 IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
1762 extension.stopRewriteSession(session);
1763 event.setPartitionChange(partitioning, 0, getLength());
1764 }
1765 }
1766 if (!event.isEmpty())
1767 fireDocumentPartitioningChanged(event);
1768 }
1769 }
1770
1771 /*
1772 * @see org.eclipse.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
1773 * @since 3.1
1774 */
1775 public void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
1776 Assert.isNotNull(cast(Object)listener);
1777 if (! fDocumentRewriteSessionListeners.contains(cast(Object)listener))
1778 fDocumentRewriteSessionListeners.add(cast(Object)listener);
1779 }
1780
1781 /*
1782 * @see org.eclipse.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
1783 * @since 3.1
1784 */
1785 public void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
1786 Assert.isNotNull(cast(Object)listener);
1787 fDocumentRewriteSessionListeners.remove(cast(Object)listener);
1788 }
1789
1790 /**
1791 * Checks the state for the given partitioner and stops the
1792 * active rewrite session.
1793 *
1794 * @param partitioner the document partitioner to be checked
1795 * @param partitioning the document partitioning the partitioner is registered for
1796 * @since 3.1
1797 */
1798 protected final void checkStateOfPartitioner(IDocumentPartitioner partitioner, String partitioning) {
1799 if (!( cast(IDocumentPartitionerExtension3)partitioner ))
1800 return;
1801
1802 IDocumentPartitionerExtension3 extension= cast(IDocumentPartitionerExtension3) partitioner;
1803 DocumentRewriteSession session= extension.getActiveRewriteSession();
1804 if (session !is null) {
1805 extension.stopRewriteSession(session);
1806
1807 if (DEBUG)
1808 System.out_.println(Format("AbstractDocument: Flushing rewrite session for partition type: {}", partitioning)); //$NON-NLS-1$
1809
1810 DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
1811 event.setPartitionChange(partitioning, 0, getLength());
1812 fireDocumentPartitioningChanged(event);
1813 }
1814 }
1815
1816 /**
1817 * Returns all positions of the given category that are inside the given region.
1818 *
1819 * @param category the position category
1820 * @param offset the start position of the region, must be >= 0
1821 * @param length the length of the region, must be >= 0
1822 * @param canStartBefore if <code>true</code> then positions are included
1823 * which start before the region if they end at or after the regions start
1824 * @param canEndAfter if <code>true</code> then positions are included
1825 * which end after the region if they start at or before the regions end
1826 * @return all positions inside the region of the given category
1827 * @throws BadPositionCategoryException if category is undefined in this document
1828 * @since 3.4
1829 */
1830 public Position[] getPositions(String category, int offset, int length, bool canStartBefore, bool canEndAfter) {
1831 if (canStartBefore && canEndAfter || (!canStartBefore && !canEndAfter)) {
1832 List documentPositions;
1833 if (canStartBefore && canEndAfter) {
1834 if (offset < getLength() / 2) {
1835 documentPositions= getStartingPositions(category, 0, offset + length);
1836 } else {
1837 documentPositions= getEndingPositions(category, offset, getLength() - offset + 1);
1838 }
1839 } else {
1840 documentPositions= getStartingPositions(category, offset, length);
1841 }
1842
1843 ArrayList list= new ArrayList(documentPositions.size());
1844
1845 Position region= new Position(offset, length);
1846
1847 for (Iterator iterator= documentPositions.iterator(); iterator.hasNext();) {
1848 Position position= cast(Position) iterator.next();
1849 if (isWithinRegion(region, position, canStartBefore, canEndAfter)) {
1850 list.add(position);
1851 }
1852 }
1853
1854 Position[] positions= new Position[list.size()];
1855 list.toArray(positions);
1856 return positions;
1857 } else if (canStartBefore) {
1858 List list= getEndingPositions(category, offset, length);
1859 Position[] positions= new Position[list.size()];
1860 list.toArray(positions);
1861 return positions;
1862 } else {
1863 Assert.isLegal(canEndAfter && !canStartBefore);
1864
1865 List list= getStartingPositions(category, offset, length);
1866 Position[] positions= new Position[list.size()];
1867 list.toArray(positions);
1868 return positions;
1869 }
1870 }
1871
1872 /*
1873 * @since 3.4
1874 */
1875 private bool isWithinRegion(Position region, Position position, bool canStartBefore, bool canEndAfter) {
1876 if (canStartBefore && canEndAfter) {
1877 return region.overlapsWith(position.getOffset(), position.getLength());
1878 } else if (canStartBefore) {
1879 return region.includes(position.getOffset() + position.getLength() - 1);
1880 } else if (canEndAfter) {
1881 return region.includes(position.getOffset());
1882 } else {
1883 int start= position.getOffset();
1884 return region.includes(start) && region.includes(start + position.getLength() - 1);
1885 }
1886 }
1887
1888 /**
1889 * A list of positions in the given category with an offset inside the given
1890 * region. The order of the positions is arbitrary.
1891 *
1892 * @param category the position category
1893 * @param offset the offset of the region
1894 * @param length the length of the region
1895 * @return a list of the positions in the region
1896 * @throws BadPositionCategoryException if category is undefined in this document
1897 * @since 3.4
1898 */
1899 private List getStartingPositions(String category, int offset, int length) {
1900 List positions= cast(List) fPositions.get(category);
1901 if (positions is null)
1902 throw new BadPositionCategoryException();
1903
1904 int indexStart= computeIndexInPositionList(positions, offset, true);
1905 int indexEnd= computeIndexInPositionList(positions, offset + length, true);
1906
1907 return positions.subList(indexStart, indexEnd);
1908 }
1909
1910 /**
1911 * A list of positions in the given category with an end position inside
1912 * the given region. The order of the positions is arbitrary.
1913 *
1914 * @param category the position category
1915 * @param offset the offset of the region
1916 * @param length the length of the region
1917 * @return a list of the positions in the region
1918 * @throws BadPositionCategoryException if category is undefined in this document
1919 * @since 3.4
1920 */
1921 private List getEndingPositions(String category, int offset, int length) {
1922 List positions= cast(List) fEndPositions.get(category);
1923 if (positions is null)
1924 throw new BadPositionCategoryException();
1925
1926 int indexStart= computeIndexInPositionList(positions, offset, false);
1927 int indexEnd= computeIndexInPositionList(positions, offset + length, false);
1928
1929 return positions.subList(indexStart, indexEnd);
1930 }
1931
1932
1933 }