comparison dwtx/jface/text/AbstractDocument.d @ 129:eb30df5ca28b

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