Mercurial > projects > dwt-addons
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 } |