Mercurial > projects > dwt-addons
annotate dwtx/jface/text/projection/ProjectionDocument.d @ 153:f70d9508c95c
Fix java Collection imports
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 25 Aug 2008 00:27:31 +0200 |
parents | 75302ef3f92f |
children | 3678e4f1a766 |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.text.projection.ProjectionDocument; | |
14 | |
131 | 15 import dwtx.jface.text.projection.ProjectionMapping; // packageimport |
16 import dwtx.jface.text.projection.ChildDocumentManager; // packageimport | |
17 import dwtx.jface.text.projection.SegmentUpdater; // packageimport | |
18 import dwtx.jface.text.projection.Segment; // packageimport | |
19 import dwtx.jface.text.projection.FragmentUpdater; // packageimport | |
20 import dwtx.jface.text.projection.ProjectionDocumentEvent; // packageimport | |
21 import dwtx.jface.text.projection.ChildDocument; // packageimport | |
22 import dwtx.jface.text.projection.IMinimalMapping; // packageimport | |
23 import dwtx.jface.text.projection.Fragment; // packageimport | |
24 import dwtx.jface.text.projection.ProjectionTextStore; // packageimport | |
25 import dwtx.jface.text.projection.ProjectionDocumentManager; // packageimport | |
26 | |
27 | |
129 | 28 import dwt.dwthelper.utils; |
29 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
146
diff
changeset
|
30 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
146
diff
changeset
|
31 |
129 | 32 |
33 import dwtx.jface.text.AbstractDocument; | |
34 import dwtx.jface.text.BadLocationException; | |
35 import dwtx.jface.text.BadPositionCategoryException; | |
36 import dwtx.jface.text.DefaultLineTracker; | |
37 import dwtx.jface.text.DocumentEvent; | |
38 import dwtx.jface.text.IDocument; | |
39 import dwtx.jface.text.IDocumentExtension; | |
40 import dwtx.jface.text.IDocumentInformationMapping; | |
41 import dwtx.jface.text.IDocumentListener; | |
42 import dwtx.jface.text.ILineTracker; | |
43 import dwtx.jface.text.IRegion; | |
44 import dwtx.jface.text.ITextStore; | |
45 import dwtx.jface.text.Position; | |
46 import dwtx.jface.text.Region; | |
47 import dwtx.jface.text.TextUtilities; | |
48 | |
49 | |
50 /** | |
51 * A <code>ProjectionDocument</code> represents a projection of its master | |
52 * document. The contents of a projection document is a sequence of fragments of | |
53 * the master document, i.e. the projection document can be thought as being | |
54 * constructed from the master document by not copying the whole master document | |
55 * but omitting several ranges of the master document. | |
56 * <p> | |
57 * The projection document indirectly utilizes its master document as | |
58 * <code>ITextStore</code> by means of a <code>ProjectionTextStore</code>. | |
59 * <p> | |
60 * The content of a projection document can be changed in two ways. Either by a | |
61 * text replace applied to the master document or the projection document. Or by | |
62 * changing the projection between the master document and the projection | |
63 * document. For the latter the two methods <code>addMasterDocumentRange</code> | |
64 * and <code>removeMasterDocumentRange</code> are provided. For any | |
65 * manipulation, the projection document sends out a | |
66 * {@link dwtx.jface.text.projection.ProjectionDocumentEvent} describing | |
67 * the change. | |
68 * <p> | |
69 * Clients are not supposed to directly instantiate this class. In order to | |
70 * obtain a projection document, a | |
71 * {@link dwtx.jface.text.projection.ProjectionDocumentManager}should be | |
72 * used. This class is not intended to be subclassed outside of its origin | |
73 * package.</p> | |
74 * | |
75 * @since 3.0 | |
76 * @noinstantiate This class is not intended to be instantiated by clients. | |
77 * @noextend This class is not intended to be subclassed by clients. | |
78 */ | |
79 public class ProjectionDocument : AbstractDocument { | |
80 | |
81 | |
82 /** | |
83 * Prefix of the name of the position category used to keep track of the master | |
84 * document's fragments that correspond to the segments of the projection | |
85 * document. | |
86 */ | |
146 | 87 private const static String FRAGMENTS_CATEGORY_PREFIX= "__fragmentsCategory"; //$NON-NLS-1$ |
129 | 88 |
89 /** | |
90 * Name of the position category used to keep track of the project | |
91 * document's segments that correspond to the fragments of the master | |
92 * document. | |
93 */ | |
146 | 94 private const static String SEGMENTS_CATEGORY= "__segmentsCategory"; //$NON-NLS-1$ |
129 | 95 |
96 | |
97 /** The master document */ | |
98 private IDocument fMasterDocument; | |
99 /** The master document as document extension */ | |
100 private IDocumentExtension fMasterDocumentExtension; | |
101 /** The fragments' position category */ | |
102 private String fFragmentsCategory; | |
103 /** The segment's position category */ | |
104 private String fSegmentsCategory; | |
105 /** The document event issued by the master document */ | |
106 private DocumentEvent fMasterEvent; | |
107 /** The document event to be issued by the projection document */ | |
108 private ProjectionDocumentEvent fSlaveEvent; | |
109 /** The original document event generated by a direct manipulation of this projection document */ | |
110 private DocumentEvent fOriginalEvent; | |
111 /** Indicates whether the projection document initiated a master document update or not */ | |
112 private bool fIsUpdating= false; | |
113 /** Indicated whether the projection document is in auto expand mode nor not */ | |
114 private bool fIsAutoExpanding= false; | |
115 /** The position updater for the segments */ | |
116 private SegmentUpdater fSegmentUpdater; | |
117 /** The position updater for the fragments */ | |
118 private FragmentUpdater fFragmentsUpdater; | |
119 /** The projection mapping */ | |
120 private ProjectionMapping fMapping; | |
121 | |
122 /** | |
123 * Creates a projection document for the given master document. | |
124 * | |
125 * @param masterDocument the master document | |
126 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
127 public this(IDocument masterDocument) { |
129 | 128 super(); |
129 | |
130 fMasterDocument= masterDocument; | |
138 | 131 if ( cast(IDocumentExtension)fMasterDocument ) |
134 | 132 fMasterDocumentExtension= cast(IDocumentExtension) fMasterDocument; |
129 | 133 |
134 fSegmentsCategory= SEGMENTS_CATEGORY; | |
135 fFragmentsCategory= FRAGMENTS_CATEGORY_PREFIX + hashCode(); | |
136 fMasterDocument.addPositionCategory(fFragmentsCategory); | |
137 fFragmentsUpdater= new FragmentUpdater(fFragmentsCategory); | |
138 fMasterDocument.addPositionUpdater(fFragmentsUpdater); | |
139 | |
140 fMapping= new ProjectionMapping(masterDocument, fFragmentsCategory, this, fSegmentsCategory); | |
141 | |
142 ITextStore s= new ProjectionTextStore(masterDocument, fMapping); | |
143 ILineTracker tracker= new DefaultLineTracker(); | |
144 | |
145 setTextStore(s); | |
146 setLineTracker(tracker); | |
147 | |
148 completeInitialization(); | |
149 | |
150 initializeProjection(); | |
151 tracker.set(s.get(0, s.getLength())); | |
152 } | |
153 | |
154 /** | |
155 * Disposes this projection document. | |
156 */ | |
157 public void dispose() { | |
158 fMasterDocument.removePositionUpdater(fFragmentsUpdater); | |
159 try { | |
160 fMasterDocument.removePositionCategory(fFragmentsCategory); | |
161 } catch (BadPositionCategoryException x) { | |
162 // allow multiple dispose calls | |
163 } | |
164 } | |
165 | |
166 private void internalError() { | |
167 throw new IllegalStateException(); | |
168 } | |
169 | |
170 /** | |
171 * Returns the fragments of the master documents. | |
172 * | |
173 * @return the fragment of the master document | |
174 */ | |
175 protected final Position[] getFragments() { | |
176 try { | |
177 return fMasterDocument.getPositions(fFragmentsCategory); | |
178 } catch (BadPositionCategoryException e) { | |
179 internalError(); | |
180 } | |
181 // unreachable | |
182 return null; | |
183 } | |
184 | |
185 /** | |
186 * Returns the segments of this projection document. | |
187 * | |
188 * @return the segments of this projection document | |
189 */ | |
190 protected final Position[] getSegments() { | |
191 try { | |
192 return getPositions(fSegmentsCategory); | |
193 } catch (BadPositionCategoryException e) { | |
194 internalError(); | |
195 } | |
196 // unreachable | |
197 return null; | |
198 } | |
199 | |
200 /** | |
201 * Returns the projection mapping used by this document. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
202 * |
129 | 203 * @return the projection mapping used by this document |
204 * @deprecated As of 3.4, replaced by {@link #getDocumentInformationMapping()} | |
205 */ | |
206 public ProjectionMapping getProjectionMapping(){ | |
207 return fMapping; | |
208 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
209 |
129 | 210 /** |
211 * Returns the projection mapping used by this document. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
212 * |
129 | 213 * @return the projection mapping used by this document |
214 * @since 3.4 | |
215 */ | |
216 public IDocumentInformationMapping getDocumentInformationMapping() { | |
217 return fMapping; | |
218 } | |
219 | |
220 /** | |
221 * Returns the master document of this projection document. | |
222 * | |
223 * @return the master document of this projection document | |
224 */ | |
225 public IDocument getMasterDocument() { | |
226 return fMasterDocument; | |
227 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
228 |
129 | 229 /* |
230 * @see dwtx.jface.text.IDocumentExtension4#getDefaultLineDelimiter() | |
231 * @since 3.1 | |
232 */ | |
233 public String getDefaultLineDelimiter() { | |
234 return TextUtilities.getDefaultLineDelimiter(fMasterDocument); | |
235 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
236 |
129 | 237 /** |
238 * Initializes the projection document from the master document based on | |
239 * the master's fragments. | |
240 */ | |
241 private void initializeProjection() { | |
242 | |
243 try { | |
244 | |
245 addPositionCategory(fSegmentsCategory); | |
246 fSegmentUpdater= new SegmentUpdater(fSegmentsCategory); | |
247 addPositionUpdater(fSegmentUpdater); | |
248 | |
249 int offset= 0; | |
250 Position[] fragments= getFragments(); | |
251 for (int i= 0; i < fragments.length; i++) { | |
134 | 252 Fragment fragment= cast(Fragment) fragments[i]; |
129 | 253 Segment segment= new Segment(offset, fragment.getLength()); |
254 segment.fragment= fragment; | |
255 addPosition(fSegmentsCategory, segment); | |
256 offset += fragment.length; | |
257 } | |
258 | |
259 } catch (BadPositionCategoryException x) { | |
260 internalError(); | |
261 } catch (BadLocationException x) { | |
262 internalError(); | |
263 } | |
264 } | |
265 | |
266 /** | |
267 * Creates a segment for the given fragment at the given position inside the list of segments. | |
268 * | |
269 * @param fragment the corresponding fragment | |
270 * @param index the index in the list of segments | |
271 * @return the created segment | |
272 * @throws BadLocationException in case the fragment is invalid | |
273 * @throws BadPositionCategoryException in case the segment category is invalid | |
274 */ | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
275 private Segment createSegmentFor(Fragment fragment, int index) { |
129 | 276 |
277 int offset= 0; | |
278 if (index > 0) { | |
279 Position[] segments= getSegments(); | |
134 | 280 Segment segment= cast(Segment) segments[index - 1]; |
129 | 281 offset= segment.getOffset() + segment.getLength(); |
282 } | |
283 | |
284 Segment segment= new Segment(offset, 0); | |
285 segment.fragment= fragment; | |
286 fragment.segment= segment; | |
287 addPosition(fSegmentsCategory, segment); | |
288 return segment; | |
289 } | |
290 | |
291 /** | |
292 * Adds the given range of the master document to this projection document. | |
293 * | |
294 * @param offsetInMaster offset of the master document range | |
295 * @param lengthInMaster length of the master document range | |
296 * @param masterDocumentEvent the master document event that causes this | |
297 * projection change or <code>null</code> if none | |
298 * @throws BadLocationException if the given range is invalid in the master | |
299 * document | |
300 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
301 private void internalAddMasterDocumentRange(int offsetInMaster, int lengthInMaster, DocumentEvent masterDocumentEvent) { |
129 | 302 if (lengthInMaster is 0) |
303 return; | |
304 | |
305 try { | |
306 | |
307 Position[] fragments= getFragments(); | |
308 int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offsetInMaster); | |
309 | |
310 Fragment left= null; | |
311 Fragment right= null; | |
312 | |
313 if (index < fragments.length) { | |
134 | 314 Fragment fragment= cast(Fragment) fragments[index]; |
129 | 315 if (offsetInMaster is fragment.offset) |
316 if (fragment.length is 0) // the fragment does not overlap - it is a zero-length fragment at the same offset | |
317 left= fragment; | |
318 else | |
319 throw new IllegalArgumentException("overlaps with existing fragment"); //$NON-NLS-1$ | |
320 if (offsetInMaster + lengthInMaster is fragment.offset) | |
321 right= fragment; | |
322 } | |
323 | |
324 if (0 < index && index <= fragments.length) { | |
134 | 325 Fragment fragment= cast(Fragment) fragments[index - 1]; |
129 | 326 if (fragment.includes(offsetInMaster)) |
327 throw new IllegalArgumentException("overlaps with existing fragment"); //$NON-NLS-1$ | |
328 if (fragment.getOffset() + fragment.getLength() is offsetInMaster) | |
329 left= fragment; | |
330 } | |
331 | |
332 int offsetInSlave= 0; | |
333 if (index > 0) { | |
134 | 334 Fragment fragment= cast(Fragment) fragments[index - 1]; |
129 | 335 Segment segment= fragment.segment; |
336 offsetInSlave= segment.getOffset() + segment.getLength(); | |
337 } | |
338 | |
339 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, offsetInSlave, 0, fMasterDocument.get(offsetInMaster, lengthInMaster), offsetInMaster, lengthInMaster, masterDocumentEvent); | |
340 super.fireDocumentAboutToBeChanged(event); | |
341 | |
342 // check for neighboring fragment | |
343 if (left !is null && right !is null) { | |
344 | |
345 int endOffset= right.getOffset() + right.getLength(); | |
346 left.setLength(endOffset - left.getOffset()); | |
347 left.segment.setLength(left.segment.getLength() + right.segment.getLength()); | |
348 | |
349 removePosition(fSegmentsCategory, right.segment); | |
350 fMasterDocument.removePosition(fFragmentsCategory, right); | |
351 | |
352 } else if (left !is null) { | |
353 int endOffset= offsetInMaster +lengthInMaster; | |
354 left.setLength(endOffset - left.getOffset()); | |
355 left.segment.markForStretch(); | |
356 | |
357 } else if (right !is null) { | |
358 right.setOffset(right.getOffset() - lengthInMaster); | |
359 right.setLength(right.getLength() + lengthInMaster); | |
360 right.segment.markForStretch(); | |
361 | |
362 } else { | |
363 // create a new segment | |
364 Fragment fragment= new Fragment(offsetInMaster, lengthInMaster); | |
365 fMasterDocument.addPosition(fFragmentsCategory, fragment); | |
366 Segment segment= createSegmentFor(fragment, index); | |
367 segment.markForStretch(); | |
368 } | |
369 | |
370 getTracker().replace(event.getOffset(), event.getLength(), event.getText()); | |
371 super.fireDocumentChanged(event); | |
372 | |
373 } catch (BadPositionCategoryException x) { | |
374 internalError(); | |
375 } | |
376 } | |
377 | |
378 /** | |
379 * Finds the fragment of the master document that represents the given range. | |
380 * | |
381 * @param offsetInMaster the offset of the range in the master document | |
382 * @param lengthInMaster the length of the range in the master document | |
383 * @return the fragment representing the given master document range | |
384 */ | |
385 private Fragment findFragment(int offsetInMaster, int lengthInMaster) { | |
386 Position[] fragments= getFragments(); | |
387 for (int i= 0; i < fragments.length; i++) { | |
134 | 388 Fragment f= cast(Fragment) fragments[i]; |
129 | 389 if (f.getOffset() <= offsetInMaster && offsetInMaster + lengthInMaster <= f.getOffset() + f.getLength()) |
390 return f; | |
391 } | |
392 return null; | |
393 } | |
394 | |
395 /** | |
396 * Removes the given range of the master document from this projection | |
397 * document. | |
398 * | |
399 * @param offsetInMaster the offset of the range in the master document | |
400 * @param lengthInMaster the length of the range in the master document | |
401 * | |
402 * @throws BadLocationException if the given range is not valid in the | |
403 * master document | |
404 * @throws IllegalArgumentException if the given range is not projected in | |
405 * this projection document or is not completely comprised by | |
406 * an existing fragment | |
407 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
408 private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInMaster) { |
129 | 409 try { |
410 | |
411 IRegion imageRegion= fMapping.toExactImageRegion(new Region(offsetInMaster, lengthInMaster)); | |
412 if (imageRegion is null) | |
413 throw new IllegalArgumentException(); | |
414 | |
415 Fragment fragment= findFragment(offsetInMaster, lengthInMaster); | |
416 if (fragment is null) | |
417 throw new IllegalArgumentException(); | |
418 | |
419 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), "", offsetInMaster, lengthInMaster); //$NON-NLS-1$ | |
420 super.fireDocumentAboutToBeChanged(event); | |
421 | |
422 if (fragment.getOffset() is offsetInMaster) { | |
423 fragment.setOffset(offsetInMaster + lengthInMaster); | |
424 fragment.setLength(fragment.getLength() - lengthInMaster); | |
425 } else if (fragment.getOffset() + fragment.getLength() is offsetInMaster + lengthInMaster) { | |
426 fragment.setLength(fragment.getLength() - lengthInMaster); | |
427 } else { | |
428 // split fragment into three fragments, let position updater remove it | |
429 | |
430 // add fragment for the region to be removed | |
431 Fragment newFragment= new Fragment(offsetInMaster, lengthInMaster); | |
432 Segment segment= new Segment(imageRegion.getOffset(), imageRegion.getLength()); | |
433 newFragment.segment= segment; | |
434 segment.fragment= newFragment; | |
435 fMasterDocument.addPosition(fFragmentsCategory, newFragment); | |
436 addPosition(fSegmentsCategory, segment); | |
437 | |
438 // add fragment for the remainder right of the deleted range in the original fragment | |
439 int offset= offsetInMaster + lengthInMaster; | |
440 newFragment= new Fragment(offset, fragment.getOffset() + fragment.getLength() - offset); | |
441 offset= imageRegion.getOffset() + imageRegion.getLength(); | |
442 segment= new Segment(offset, fragment.segment.getOffset() + fragment.segment.getLength() - offset); | |
443 newFragment.segment= segment; | |
444 segment.fragment= newFragment; | |
445 fMasterDocument.addPosition(fFragmentsCategory, newFragment); | |
446 addPosition(fSegmentsCategory, segment); | |
447 | |
448 // adjust length of initial fragment (the left one) | |
449 fragment.setLength(offsetInMaster - fragment.getOffset()); | |
450 fragment.segment.setLength(imageRegion.getOffset() - fragment.segment.getOffset()); | |
451 } | |
452 | |
453 getTracker().replace(event.getOffset(), event.getLength(), event.getText()); | |
454 super.fireDocumentChanged(event); | |
455 | |
456 } catch (BadPositionCategoryException x) { | |
457 internalError(); | |
458 } | |
459 } | |
460 | |
461 /** | |
462 * Returns the sequence of all master document regions which are contained | |
463 * in the given master document range and which are not yet part of this | |
464 * projection document. | |
465 * | |
466 * @param offsetInMaster the range offset in the master document | |
467 * @param lengthInMaster the range length in the master document | |
468 * @return the sequence of regions which are not yet part of the projection | |
469 * document | |
470 * @throws BadLocationException in case the given range is invalid in the | |
471 * master document | |
472 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
473 public final IRegion[] computeUnprojectedMasterRegions(int offsetInMaster, int lengthInMaster) { |
129 | 474 |
475 IRegion[] fragments= null; | |
476 IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster)); | |
477 if (imageRegion !is null) | |
478 fragments= fMapping.toExactOriginRegions(imageRegion); | |
479 | |
480 if (fragments is null || fragments.length is 0) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
481 return [ new Region(offsetInMaster, lengthInMaster) ]; |
129 | 482 |
483 List gaps= new ArrayList(); | |
484 | |
485 IRegion region= fragments[0]; | |
486 if (offsetInMaster < region.getOffset()) | |
487 gaps.add(new Region(offsetInMaster, region.getOffset() - offsetInMaster)); | |
488 | |
489 for (int i= 0; i < fragments.length - 1; i++) { | |
490 IRegion left= fragments[i]; | |
491 IRegion right= fragments[i + 1]; | |
492 int leftEnd= left.getOffset() + left.getLength(); | |
493 if (leftEnd < right.getOffset()) | |
494 gaps.add(new Region(leftEnd, right.getOffset() - leftEnd)); | |
495 } | |
496 | |
497 region= fragments[fragments.length - 1]; | |
498 int leftEnd= region.getOffset() + region.getLength(); | |
499 int rightEnd= offsetInMaster + lengthInMaster; | |
500 if (leftEnd < rightEnd) | |
501 gaps.add(new Region(leftEnd, rightEnd - leftEnd)); | |
502 | |
503 IRegion[] result= new IRegion[gaps.size()]; | |
504 gaps.toArray(result); | |
505 return result; | |
506 } | |
507 | |
508 /** | |
509 * Returns the first master document region which is contained in the given | |
510 * master document range and which is not yet part of this projection | |
511 * document. | |
512 * | |
513 * @param offsetInMaster the range offset in the master document | |
514 * @param lengthInMaster the range length in the master document | |
515 * @return the first region that is not yet part of the projection document | |
516 * @throws BadLocationException in case the given range is invalid in the | |
517 * master document | |
518 * @since 3.1 | |
519 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
520 private IRegion computeFirstUnprojectedMasterRegion(int offsetInMaster, int lengthInMaster) { |
129 | 521 |
522 IRegion[] fragments= null; | |
523 IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster)); | |
524 if (imageRegion !is null) | |
525 fragments= fMapping.toExactOriginRegions(imageRegion); | |
526 | |
527 if (fragments is null || fragments.length is 0) | |
528 return new Region(offsetInMaster, lengthInMaster); | |
529 | |
530 IRegion region= fragments[0]; | |
531 if (offsetInMaster < region.getOffset()) | |
532 return new Region(offsetInMaster, region.getOffset() - offsetInMaster); | |
533 | |
534 for (int i= 0; i < fragments.length - 1; i++) { | |
535 IRegion left= fragments[i]; | |
536 IRegion right= fragments[i + 1]; | |
537 int leftEnd= left.getOffset() + left.getLength(); | |
538 if (leftEnd < right.getOffset()) | |
539 return new Region(leftEnd, right.getOffset() - leftEnd); | |
540 } | |
541 | |
542 region= fragments[fragments.length - 1]; | |
543 int leftEnd= region.getOffset() + region.getLength(); | |
544 int rightEnd= offsetInMaster + lengthInMaster; | |
545 if (leftEnd < rightEnd) | |
546 return new Region(leftEnd, rightEnd - leftEnd); | |
547 | |
548 return null; | |
549 } | |
550 | |
551 /** | |
552 * Ensures that the given range of the master document is part of this | |
553 * projection document. | |
554 * | |
555 * @param offsetInMaster the offset of the master document range | |
556 * @param lengthInMaster the length of the master document range | |
557 * @throws BadLocationException in case the master event is not valid | |
558 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
559 public void addMasterDocumentRange(int offsetInMaster, int lengthInMaster) { |
129 | 560 addMasterDocumentRange(offsetInMaster, lengthInMaster, null); |
561 } | |
562 | |
563 /** | |
564 * Ensures that the given range of the master document is part of this | |
565 * projection document. | |
566 * | |
567 * @param offsetInMaster the offset of the master document range | |
568 * @param lengthInMaster the length of the master document range | |
569 * @param masterDocumentEvent the master document event which causes this | |
570 * projection change, or <code>null</code> if none | |
571 * @throws BadLocationException in case the master event is not valid | |
572 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
573 private void addMasterDocumentRange(int offsetInMaster, int lengthInMaster, DocumentEvent masterDocumentEvent) { |
129 | 574 /* |
575 * Calling internalAddMasterDocumentRange may cause other master ranges | |
576 * to become unfolded, resulting in re-entrant calls to this method. In | |
577 * order to not add a region twice, we have to compute the next region | |
578 * to add in every iteration. | |
579 * | |
580 * To place an upper bound on the number of iterations, we use the number | |
581 * of fragments * 2 as the limit. | |
582 */ | |
583 int limit= Math.max(getFragments().length * 2, 20); | |
584 while (true) { | |
585 if (limit-- < 0) | |
586 throw new IllegalArgumentException("safety loop termination"); //$NON-NLS-1$ | |
587 | |
588 IRegion gap= computeFirstUnprojectedMasterRegion(offsetInMaster, lengthInMaster); | |
589 if (gap is null) | |
590 return; | |
591 | |
592 internalAddMasterDocumentRange(gap.getOffset(), gap.getLength(), masterDocumentEvent); | |
593 } | |
594 } | |
595 | |
596 /** | |
597 * Ensures that the given range of the master document is not part of this | |
598 * projection document. | |
599 * | |
600 * @param offsetInMaster the offset of the master document range | |
601 * @param lengthInMaster the length of the master document range | |
602 * @throws BadLocationException in case the master event is not valid | |
603 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
604 public void removeMasterDocumentRange(int offsetInMaster, int lengthInMaster) { |
129 | 605 IRegion[] fragments= computeProjectedMasterRegions(offsetInMaster, lengthInMaster); |
606 if (fragments is null || fragments.length is 0) | |
607 return; | |
608 | |
609 for (int i= 0; i < fragments.length; i++) { | |
610 IRegion fragment= fragments[i]; | |
611 internalRemoveMasterDocumentRange(fragment.getOffset(), fragment.getLength()); | |
612 } | |
613 } | |
614 | |
615 /** | |
616 * Returns the sequence of all master document regions with are contained in the given master document | |
617 * range and which are part of this projection document. May return <code>null</code> if no such | |
618 * regions exist. | |
619 * | |
620 * @param offsetInMaster the range offset in the master document | |
621 * @param lengthInMaster the range length in the master document | |
622 * @return the sequence of regions which are part of the projection document or <code>null</code> | |
623 * @throws BadLocationException in case the given range is invalid in the master document | |
624 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
625 public final IRegion[] computeProjectedMasterRegions(int offsetInMaster, int lengthInMaster) { |
129 | 626 IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster)); |
627 return imageRegion !is null ? fMapping.toExactOriginRegions(imageRegion) : null; | |
628 } | |
629 | |
630 /** | |
631 * Returns whether this projection is being updated. | |
632 * | |
633 * @return <code>true</code> if the document is updating | |
634 */ | |
635 protected bool isUpdating() { | |
636 return fIsUpdating; | |
637 } | |
638 | |
639 /* | |
640 * @see dwtx.jface.text.IDocument#replace(int, int, java.lang.String) | |
641 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
642 public void replace(int offset, int length, String text) { |
129 | 643 try { |
644 fIsUpdating= true; | |
645 if (fMasterDocumentExtension !is null) | |
646 fMasterDocumentExtension.stopPostNotificationProcessing(); | |
647 | |
648 super.replace(offset, length, text); | |
649 | |
650 } finally { | |
651 fIsUpdating= false; | |
652 if (fMasterDocumentExtension !is null) | |
653 fMasterDocumentExtension.resumePostNotificationProcessing(); | |
654 } | |
655 } | |
656 | |
657 /* | |
658 * @see dwtx.jface.text.IDocument#set(java.lang.String) | |
659 */ | |
660 public void set(String text) { | |
661 try { | |
662 fIsUpdating= true; | |
663 if (fMasterDocumentExtension !is null) | |
664 fMasterDocumentExtension.stopPostNotificationProcessing(); | |
665 | |
666 super.set(text); | |
667 | |
668 } finally { | |
669 fIsUpdating= false; | |
670 if (fMasterDocumentExtension !is null) | |
671 fMasterDocumentExtension.resumePostNotificationProcessing(); | |
672 } | |
673 } | |
674 | |
675 /** | |
676 * Transforms a document event of the master document into a projection | |
677 * document based document event. | |
678 * | |
679 * @param masterEvent the master document event | |
680 * @return the slave document event | |
681 * @throws BadLocationException in case the master event is not valid | |
682 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
683 private ProjectionDocumentEvent normalize(DocumentEvent masterEvent) { |
129 | 684 if (!isUpdating()) { |
685 IRegion imageRegion= fMapping.toExactImageRegion(new Region(masterEvent.getOffset(), masterEvent.getLength())); | |
686 if (imageRegion !is null) | |
687 return new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), masterEvent.getText(), masterEvent); | |
688 return null; | |
689 } | |
690 | |
691 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, fOriginalEvent.getOffset(), fOriginalEvent.getLength(), fOriginalEvent.getText(), masterEvent); | |
692 fOriginalEvent= null; | |
693 return event; | |
694 } | |
695 | |
696 /** | |
697 * Ensures that when the master event affects this projection document, that the whole region described by the | |
698 * event is part of this projection document. | |
699 * | |
700 * @param masterEvent the master document event | |
701 * @return <code>true</code> if masterEvent affects this projection document | |
702 * @throws BadLocationException in case the master event is not valid | |
703 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
704 protected final bool adaptProjectionToMasterChange(DocumentEvent masterEvent) { |
129 | 705 if (!isUpdating() && fFragmentsUpdater.affectsPositions(masterEvent) || fIsAutoExpanding && masterEvent.getLength() > 0) { |
706 | |
707 addMasterDocumentRange(masterEvent.getOffset(), masterEvent.getLength(), masterEvent); | |
708 return true; | |
709 | |
710 } else if (fMapping.getImageLength() is 0 && masterEvent.getLength() is 0) { | |
711 | |
712 Position[] fragments= getFragments(); | |
713 if (fragments.length is 0) { | |
714 // there is no segment in this projection document, thus one must be created | |
715 // need to bypass the usual infrastructure as the new segment/fragment would be of length 0 and thus the segmentation be not well formed | |
716 try { | |
717 Fragment fragment= new Fragment(0, 0); | |
718 fMasterDocument.addPosition(fFragmentsCategory, fragment); | |
719 createSegmentFor(fragment, 0); | |
720 } catch (BadPositionCategoryException x) { | |
721 internalError(); | |
722 } | |
723 } | |
724 } | |
725 | |
726 return isUpdating(); | |
727 } | |
728 | |
729 /** | |
730 * When called, this projection document is informed about a forthcoming | |
731 * change of its master document. This projection document checks whether | |
732 * the master document change affects it and if so informs all document | |
733 * listeners. | |
734 * | |
735 * @param masterEvent the master document event | |
736 */ | |
737 public void masterDocumentAboutToBeChanged(DocumentEvent masterEvent) { | |
738 try { | |
739 | |
740 bool assertNotNull= adaptProjectionToMasterChange(masterEvent); | |
741 fSlaveEvent= normalize(masterEvent); | |
742 if (assertNotNull && fSlaveEvent is null) | |
743 internalError(); | |
744 | |
745 fMasterEvent= masterEvent; | |
746 if (fSlaveEvent !is null) | |
747 delayedFireDocumentAboutToBeChanged(); | |
748 | |
749 } catch (BadLocationException e) { | |
750 internalError(); | |
751 } | |
752 } | |
753 | |
754 /** | |
755 * When called, this projection document is informed about a change of its | |
756 * master document. If this projection document is affected it informs all | |
757 * of its document listeners. | |
758 * | |
759 * @param masterEvent the master document event | |
760 */ | |
761 public void masterDocumentChanged(DocumentEvent masterEvent) { | |
762 if ( !isUpdating() && masterEvent is fMasterEvent) { | |
763 if (fSlaveEvent !is null) { | |
764 try { | |
765 getTracker().replace(fSlaveEvent.getOffset(), fSlaveEvent.getLength(), fSlaveEvent.getText()); | |
766 fireDocumentChanged(fSlaveEvent); | |
767 } catch (BadLocationException e) { | |
768 internalError(); | |
769 } | |
770 } else if (ensureWellFormedSegmentation(masterEvent.getOffset())) | |
771 fMapping.projectionChanged(); | |
772 } | |
773 } | |
774 | |
775 /* | |
776 * @see dwtx.jface.text.AbstractDocument#fireDocumentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
777 */ | |
778 protected void fireDocumentAboutToBeChanged(DocumentEvent event) { | |
779 fOriginalEvent= event; | |
780 // delay it until there is a notification from the master document | |
781 // at this point, it is expensive to construct the master document information | |
782 } | |
783 | |
784 /** | |
785 * Fires the slave document event as about-to-be-changed event to all registered listeners. | |
786 */ | |
787 private void delayedFireDocumentAboutToBeChanged() { | |
788 super.fireDocumentAboutToBeChanged(fSlaveEvent); | |
789 } | |
790 | |
791 /** | |
792 * Ignores the given event and sends the semantically equal slave document event instead. | |
793 * | |
794 * @param event the event to be ignored | |
795 */ | |
796 protected void fireDocumentChanged(DocumentEvent event) { | |
797 super.fireDocumentChanged(fSlaveEvent); | |
798 } | |
799 | |
800 /* | |
801 * @see dwtx.jface.text.AbstractDocument#updateDocumentStructures(dwtx.jface.text.DocumentEvent) | |
802 */ | |
803 protected void updateDocumentStructures(DocumentEvent event) { | |
804 super.updateDocumentStructures(event); | |
805 ensureWellFormedSegmentation(computeAnchor(event)); | |
806 fMapping.projectionChanged(); | |
807 } | |
808 | |
809 private int computeAnchor(DocumentEvent event) { | |
138 | 810 if ( cast(ProjectionDocumentEvent)event ) { |
134 | 811 ProjectionDocumentEvent slave= cast(ProjectionDocumentEvent) event; |
129 | 812 Object changeType= slave.getChangeType(); |
813 if (ProjectionDocumentEvent.CONTENT_CHANGE is changeType) { | |
814 DocumentEvent master= slave.getMasterEvent(); | |
815 if (master !is null) | |
816 return master.getOffset(); | |
817 } else if (ProjectionDocumentEvent.PROJECTION_CHANGE is changeType) { | |
818 return slave.getMasterOffset(); | |
819 } | |
820 } | |
821 return -1; | |
822 } | |
823 | |
824 private bool ensureWellFormedSegmentation(int anchorOffset) { | |
825 bool changed= false; | |
826 Position[] segments= getSegments(); | |
827 for (int i= 0; i < segments.length; i++) { | |
134 | 828 Segment segment= cast(Segment) segments[i]; |
129 | 829 if (segment.isDeleted() || segment.getLength() is 0) { |
830 try { | |
831 removePosition(fSegmentsCategory, segment); | |
832 fMasterDocument.removePosition(fFragmentsCategory, segment.fragment); | |
833 changed= true; | |
834 } catch (BadPositionCategoryException e) { | |
835 internalError(); | |
836 } | |
837 } else if (i < segments.length - 1) { | |
134 | 838 Segment next= cast(Segment) segments[i + 1]; |
129 | 839 if (next.isDeleted() || next.getLength() is 0) |
840 continue; | |
841 Fragment fragment= segment.fragment; | |
842 if (fragment.getOffset() + fragment.getLength() is next.fragment.getOffset()) { | |
843 // join fragments and their corresponding segments | |
844 segment.setLength(segment.getLength() + next.getLength()); | |
845 fragment.setLength(fragment.getLength() + next.fragment.getLength()); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
846 next.delete_(); |
129 | 847 } |
848 } | |
849 } | |
850 | |
851 if (changed && anchorOffset !is -1) { | |
852 Position[] changedSegments= getSegments(); | |
853 if (changedSegments is null || changedSegments.length is 0) { | |
854 Fragment fragment= new Fragment(anchorOffset, 0); | |
855 try { | |
856 fMasterDocument.addPosition(fFragmentsCategory, fragment); | |
857 createSegmentFor(fragment, 0); | |
858 } catch (BadLocationException e) { | |
859 internalError(); | |
860 } catch (BadPositionCategoryException e) { | |
861 internalError(); | |
862 } | |
863 } | |
864 } | |
865 | |
866 return changed; | |
867 } | |
868 | |
869 /* | |
870 * @see IDocumentExtension#registerPostNotificationReplace(IDocumentListener, IDocumentExtension.IReplace) | |
871 */ | |
872 public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) { | |
873 if (!isUpdating()) | |
874 throw new UnsupportedOperationException(); | |
875 super.registerPostNotificationReplace(owner, replace); | |
876 } | |
877 | |
878 /** | |
879 * Sets the auto expand mode for this document. | |
880 * | |
881 * @param autoExpandMode <code>true</code> if auto-expanding | |
882 */ | |
883 public void setAutoExpandMode(bool autoExpandMode) { | |
884 fIsAutoExpanding= autoExpandMode; | |
885 } | |
886 | |
887 /** | |
888 * Replaces all master document ranges with the given master document range. | |
889 * | |
890 * @param offsetInMaster the offset in the master document | |
891 * @param lengthInMaster the length in the master document | |
892 * @throws BadLocationException if the given range of the master document is not valid | |
893 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
894 public void replaceMasterDocumentRanges(int offsetInMaster, int lengthInMaster) { |
129 | 895 try { |
896 | |
897 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, 0, fMapping.getImageLength(), fMasterDocument.get(offsetInMaster, lengthInMaster), offsetInMaster, lengthInMaster); | |
898 super.fireDocumentAboutToBeChanged(event); | |
899 | |
900 Position[] fragments= getFragments(); | |
901 for (int i= 0; i < fragments.length; i++) { | |
134 | 902 Fragment fragment= cast(Fragment) fragments[i]; |
129 | 903 fMasterDocument.removePosition(fFragmentsCategory, fragment); |
904 removePosition(fSegmentsCategory, fragment.segment); | |
905 } | |
906 | |
907 Fragment fragment= new Fragment(offsetInMaster, lengthInMaster); | |
908 Segment segment= new Segment(0, 0); | |
909 segment.fragment= fragment; | |
910 fragment.segment= segment; | |
911 fMasterDocument.addPosition(fFragmentsCategory, fragment); | |
912 addPosition(fSegmentsCategory, segment); | |
913 | |
914 getTracker().set(fMasterDocument.get(offsetInMaster, lengthInMaster)); | |
915 super.fireDocumentChanged(event); | |
916 | |
917 } catch (BadPositionCategoryException x) { | |
918 internalError(); | |
919 } | |
920 } | |
921 } |