comparison dwtx/jface/text/rules/RuleBasedPartitioner.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, 2006 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.rules.RuleBasedPartitioner;
15
16 import dwt.dwthelper.utils;
17
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import dwtx.core.runtime.Assert;
23 import dwtx.jface.text.BadLocationException;
24 import dwtx.jface.text.BadPositionCategoryException;
25 import dwtx.jface.text.DefaultPositionUpdater;
26 import dwtx.jface.text.DocumentEvent;
27 import dwtx.jface.text.IDocument;
28 import dwtx.jface.text.IDocumentPartitioner;
29 import dwtx.jface.text.IDocumentPartitionerExtension;
30 import dwtx.jface.text.IDocumentPartitionerExtension2;
31 import dwtx.jface.text.IRegion;
32 import dwtx.jface.text.ITypedRegion;
33 import dwtx.jface.text.Position;
34 import dwtx.jface.text.Region;
35 import dwtx.jface.text.TextUtilities;
36 import dwtx.jface.text.TypedPosition;
37 import dwtx.jface.text.TypedRegion;
38
39
40
41 /**
42 * A standard implementation of a syntax driven document partitioner.
43 * It uses a rule based scanner to scan the document and to determine
44 * the document's partitioning. The tokens returned by the rules the
45 * scanner is configured with are supposed to return the partition type
46 * as their data. The partitioner remembers the document's partitions
47 * in the document itself rather than maintaining its own data structure.
48 *
49 * @see IRule
50 * @see RuleBasedScanner
51 *
52 * @deprecated use <code>FastPartitioner</code> instead
53 */
54 public class RuleBasedPartitioner : IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2 {
55
56 /**
57 * The position category this partitioner uses to store the document's partitioning information
58 * @deprecated As of 3.0, use <code>getManagingPositionCategories()</code>.
59 */
60 public final static String CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$
61
62
63 /** The partitioner's scanner */
64 protected RuleBasedScanner fScanner;
65 /** The legal content types of this partitioner */
66 protected String[] fLegalContentTypes;
67 /** The partitioner's document */
68 protected IDocument fDocument;
69 /** The document length before a document change occurred */
70 protected int fPreviousDocumentLength;
71 /** The position updater used to for the default updating of partitions */
72 protected DefaultPositionUpdater fPositionUpdater;
73 /** The offset at which the first changed partition starts */
74 protected int fStartOffset;
75 /** The offset at which the last changed partition ends */
76 protected int fEndOffset;
77 /**The offset at which a partition has been deleted */
78 protected int fDeleteOffset;
79 /**
80 * The position category for managing partitioning information.
81 * @since 3.0
82 */
83 private String fPositionCategory;
84
85
86 /**
87 * Creates a new partitioner that uses the given scanner and may return
88 * partitions of the given legal content types.
89 *
90 * @param scanner the scanner this partitioner is supposed to use
91 * @param legalContentTypes the legal content types of this partitioner
92 */
93 public RuleBasedPartitioner(RuleBasedScanner scanner, String[] legalContentTypes) {
94 fScanner= scanner;
95 fLegalContentTypes= TextUtilities.copy(legalContentTypes);
96 fPositionCategory= CONTENT_TYPES_CATEGORY + hashCode();
97 fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
98 }
99
100 /*
101 * @see dwtx.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories()
102 * @since 3.0
103 */
104 public String[] getManagingPositionCategories() {
105 return new String[] { fPositionCategory };
106 }
107
108 /*
109 * @see IDocumentPartitioner#connect
110 */
111 public void connect(IDocument document) {
112 Assert.isNotNull(document);
113 Assert.isTrue(!document.containsPositionCategory(fPositionCategory));
114
115 fDocument= document;
116 fDocument.addPositionCategory(fPositionCategory);
117
118 initialize();
119 }
120
121 /**
122 * Performs the initial partitioning of the partitioner's document.
123 */
124 protected void initialize() {
125
126 fScanner.setRange(fDocument, 0, fDocument.getLength());
127
128 try {
129 IToken token= fScanner.nextToken();
130 while (!token.isEOF()) {
131
132 String contentType= getTokenContentType(token);
133
134 if (isSupportedContentType(contentType)) {
135 TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType);
136 fDocument.addPosition(fPositionCategory, p);
137 }
138
139 token= fScanner.nextToken();
140 }
141 } catch (BadLocationException x) {
142 // cannot happen as offsets come from scanner
143 } catch (BadPositionCategoryException x) {
144 // cannot happen if document has been connected before
145 }
146 }
147
148 /*
149 * @see IDocumentPartitioner#disconnect
150 */
151 public void disconnect() {
152
153 Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory));
154
155 try {
156 fDocument.removePositionCategory(fPositionCategory);
157 } catch (BadPositionCategoryException x) {
158 // can not happen because of Assert
159 }
160 }
161
162 /*
163 * @see IDocumentPartitioner#documentAboutToBeChanged
164 */
165 public void documentAboutToBeChanged(DocumentEvent e) {
166
167 Assert.isTrue(e.getDocument() is fDocument);
168
169 fPreviousDocumentLength= e.getDocument().getLength();
170 fStartOffset= -1;
171 fEndOffset= -1;
172 fDeleteOffset= -1;
173 }
174
175 /*
176 * @see IDocumentPartitioner#documentChanged
177 */
178 public bool documentChanged(DocumentEvent e) {
179 IRegion region= documentChanged2(e);
180 return (region !is null);
181 }
182
183 /**
184 * Helper method for tracking the minimal region containing all partition changes.
185 * If <code>offset</code> is smaller than the remembered offset, <code>offset</code>
186 * will from now on be remembered. If <code>offset + length</code> is greater than
187 * the remembered end offset, it will be remembered from now on.
188 *
189 * @param offset the offset
190 * @param length the length
191 */
192 private void rememberRegion(int offset, int length) {
193 // remember start offset
194 if (fStartOffset is -1)
195 fStartOffset= offset;
196 else if (offset < fStartOffset)
197 fStartOffset= offset;
198
199 // remember end offset
200 int endOffset= offset + length;
201 if (fEndOffset is -1)
202 fEndOffset= endOffset;
203 else if (endOffset > fEndOffset)
204 fEndOffset= endOffset;
205 }
206
207 /**
208 * Remembers the given offset as the deletion offset.
209 *
210 * @param offset the offset
211 */
212 private void rememberDeletedOffset(int offset) {
213 fDeleteOffset= offset;
214 }
215
216 /**
217 * Creates the minimal region containing all partition changes using the
218 * remembered offset, end offset, and deletion offset.
219 * @return the minimal region containing all the partition changes
220 */
221 private IRegion createRegion() {
222 if (fDeleteOffset is -1) {
223 if (fStartOffset is -1 || fEndOffset is -1)
224 return null;
225 return new Region(fStartOffset, fEndOffset - fStartOffset);
226 } else if (fStartOffset is -1 || fEndOffset is -1) {
227 return new Region(fDeleteOffset, 0);
228 } else {
229 int offset= Math.min(fDeleteOffset, fStartOffset);
230 int endOffset= Math.max(fDeleteOffset, fEndOffset);
231 return new Region(offset, endOffset - offset);
232 }
233 }
234
235 /*
236 * @see IDocumentPartitionerExtension#documentChanged2(DocumentEvent)
237 * @since 2.0
238 */
239 public IRegion documentChanged2(DocumentEvent e) {
240
241 try {
242
243 IDocument d= e.getDocument();
244 Position[] category= d.getPositions(fPositionCategory);
245 int first= 0;
246 int reparseStart= 0;
247 int originalSize= category.length;
248
249 if (originalSize > 0) {
250
251 /*
252 * determine character position at which the scanner starts:
253 * first position behind the last non-default partition the actual position is not involved with
254 */
255
256 first= d.computeIndexInCategory(fPositionCategory, e.getOffset());
257
258 Position p= null;
259 do {
260 --first;
261 if (first < 0)
262 break;
263
264 p= category[first];
265
266 } while (p.overlapsWith(e.getOffset(), e.getLength()) ||
267 (e.getOffset() is fPreviousDocumentLength &&
268 (p.getOffset() + p.getLength() is fPreviousDocumentLength)));
269
270 fPositionUpdater.update(e);
271 for (int i= 0; i < category.length; i++) {
272 p= category[i];
273 if (p.isDeleted) {
274 rememberDeletedOffset(e.getOffset());
275 break;
276 }
277 }
278 category= d.getPositions(fPositionCategory);
279
280 if (first >= 0) {
281 p= category[first];
282 reparseStart= p.getOffset() + p.getLength();
283 }
284
285 ++first;
286 }
287
288 fScanner.setRange(d, reparseStart, d.getLength() - reparseStart);
289
290 int lastScannedPosition= reparseStart;
291 IToken token= fScanner.nextToken();
292
293 while (!token.isEOF()) {
294
295
296 String contentType= getTokenContentType(token);
297
298 if (!isSupportedContentType(contentType)) {
299 token= fScanner.nextToken();
300 continue;
301 }
302
303 int start= fScanner.getTokenOffset();
304 int length= fScanner.getTokenLength();
305
306 lastScannedPosition= start + length - 1;
307
308 // remove all affected positions
309 while (first < category.length) {
310 TypedPosition p= (TypedPosition) category[first];
311 if (lastScannedPosition >= p.offset + p.length ||
312 (p.overlapsWith(start, length) &&
313 (!d.containsPosition(fPositionCategory, start, length) ||
314 !contentType.equals(p.getType())))) {
315
316 rememberRegion(p.offset, p.length);
317 d.removePosition(fPositionCategory, p);
318 ++ first;
319
320 } else
321 break;
322 }
323
324 // if position already exists we are done
325 if (d.containsPosition(fPositionCategory, start, length))
326 return createRegion();
327
328 // insert the new type position
329 try {
330 d.addPosition(fPositionCategory, new TypedPosition(start, length, contentType));
331 rememberRegion(start, length);
332 } catch (BadPositionCategoryException x) {
333 } catch (BadLocationException x) {
334 }
335
336 token= fScanner.nextToken();
337 }
338
339
340 // remove all positions behind lastScannedPosition since there aren't any further types
341 if (lastScannedPosition !is reparseStart) {
342 // if this condition is not met, nothing has been scanned because of a delete
343 ++ lastScannedPosition;
344 }
345 first= d.computeIndexInCategory(fPositionCategory, lastScannedPosition);
346
347 TypedPosition p;
348 while (first < category.length) {
349 p= (TypedPosition) category[first++];
350 d.removePosition(fPositionCategory, p);
351 rememberRegion(p.offset, p.length);
352 }
353
354 } catch (BadPositionCategoryException x) {
355 // should never happen on connected documents
356 } catch (BadLocationException x) {
357 }
358
359 return createRegion();
360 }
361
362
363 /**
364 * Returns the position in the partitoner's position category which is
365 * close to the given offset. This is, the position has either an offset which
366 * is the same as the given offset or an offset which is smaller than the given
367 * offset. This method profits from the knowledge that a partitioning is
368 * a ordered set of disjoint position.
369 *
370 * @param offset the offset for which to search the closest position
371 * @return the closest position in the partitioner's category
372 */
373 protected TypedPosition findClosestPosition(int offset) {
374
375 try {
376
377 int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
378 Position[] category= fDocument.getPositions(fPositionCategory);
379
380 if (category.length is 0)
381 return null;
382
383 if (index < category.length) {
384 if (offset is category[index].offset)
385 return (TypedPosition) category[index];
386 }
387
388 if (index > 0)
389 index--;
390
391 return (TypedPosition) category[index];
392
393 } catch (BadPositionCategoryException x) {
394 } catch (BadLocationException x) {
395 }
396
397 return null;
398 }
399
400
401 /*
402 * @see IDocumentPartitioner#getContentType
403 */
404 public String getContentType(int offset) {
405
406 TypedPosition p= findClosestPosition(offset);
407 if (p !is null && p.includes(offset))
408 return p.getType();
409
410 return IDocument.DEFAULT_CONTENT_TYPE;
411 }
412
413 /*
414 * @see IDocumentPartitioner#getPartition
415 */
416 public ITypedRegion getPartition(int offset) {
417
418 try {
419
420 Position[] category = fDocument.getPositions(fPositionCategory);
421
422 if (category is null || category.length is 0)
423 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
424
425 int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
426
427 if (index < category.length) {
428
429 TypedPosition next= (TypedPosition) category[index];
430
431 if (offset is next.offset)
432 return new TypedRegion(next.getOffset(), next.getLength(), next.getType());
433
434 if (index is 0)
435 return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE);
436
437 TypedPosition previous= (TypedPosition) category[index - 1];
438 if (previous.includes(offset))
439 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
440
441 int endOffset= previous.getOffset() + previous.getLength();
442 return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
443 }
444
445 TypedPosition previous= (TypedPosition) category[category.length - 1];
446 if (previous.includes(offset))
447 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
448
449 int endOffset= previous.getOffset() + previous.getLength();
450 return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
451
452 } catch (BadPositionCategoryException x) {
453 } catch (BadLocationException x) {
454 }
455
456 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
457 }
458
459 /*
460 * @see IDocumentPartitioner#computePartitioning
461 */
462 public ITypedRegion[] computePartitioning(int offset, int length) {
463 return computePartitioning(offset, length, false);
464 }
465
466 /*
467 * @see IDocumentPartitioner#getLegalContentTypes
468 */
469 public String[] getLegalContentTypes() {
470 return TextUtilities.copy(fLegalContentTypes);
471 }
472
473 /**
474 * Returns whether the given type is one of the legal content types.
475 *
476 * @param contentType the content type to check
477 * @return <code>true</code> if the content type is a legal content type
478 */
479 protected bool isSupportedContentType(String contentType) {
480 if (contentType !is null) {
481 for (int i= 0; i < fLegalContentTypes.length; i++) {
482 if (fLegalContentTypes[i].equals(contentType))
483 return true;
484 }
485 }
486
487 return false;
488 }
489
490 /**
491 * Returns a content type encoded in the given token. If the token's
492 * data is not <code>null</code> and a string it is assumed that
493 * it is the encoded content type.
494 *
495 * @param token the token whose content type is to be determined
496 * @return the token's content type
497 */
498 protected String getTokenContentType(IToken token) {
499 Object data= token.getData();
500 if (data instanceof String)
501 return (String) data;
502 return null;
503 }
504
505 /* zero-length partition support */
506
507 /*
508 * @see dwtx.jface.text.IDocumentPartitionerExtension2#getContentType(int)
509 * @since 3.0
510 */
511 public String getContentType(int offset, bool preferOpenPartitions) {
512 return getPartition(offset, preferOpenPartitions).getType();
513 }
514
515 /*
516 * @see dwtx.jface.text.IDocumentPartitionerExtension2#getPartition(int)
517 * @since 3.0
518 */
519 public ITypedRegion getPartition(int offset, bool preferOpenPartitions) {
520 ITypedRegion region= getPartition(offset);
521 if (preferOpenPartitions) {
522 if (region.getOffset() is offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
523 if (offset > 0) {
524 region= getPartition(offset - 1);
525 if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE))
526 return region;
527 }
528 return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE);
529 }
530 }
531 return region;
532 }
533
534 /*
535 * @see dwtx.jface.text.IDocumentPartitionerExtension2#computePartitioning(int, int)
536 * @since 3.0
537 */
538 public ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions) {
539 List list= new ArrayList();
540
541 try {
542
543 int endOffset= offset + length;
544
545 Position[] category= fDocument.getPositions(fPositionCategory);
546
547 TypedPosition previous= null, current= null;
548 int start, end, gapOffset;
549 Position gap= null;
550
551 for (int i= 0; i < category.length; i++) {
552
553 current= (TypedPosition) category[i];
554
555 gapOffset= (previous !is null) ? previous.getOffset() + previous.getLength() : 0;
556 gap= new Position(gapOffset, current.getOffset() - gapOffset);
557 if ((includeZeroLengthPartitions || gap.getLength() > 0) && gap.overlapsWith(offset, length)) {
558 start= Math.max(offset, gapOffset);
559 end= Math.min(endOffset, gap.getOffset() + gap.getLength());
560 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
561 }
562
563 if (current.overlapsWith(offset, length)) {
564 start= Math.max(offset, current.getOffset());
565 end= Math.min(endOffset, current.getOffset() + current.getLength());
566 list.add(new TypedRegion(start, end - start, current.getType()));
567 }
568
569 previous= current;
570 }
571
572 if (previous !is null) {
573 gapOffset= previous.getOffset() + previous.getLength();
574 gap= new Position(gapOffset, fDocument.getLength() - gapOffset);
575 if ((includeZeroLengthPartitions || gap.getLength() > 0) && ((includeZeroLengthPartitions && offset + length is gapOffset && gap.length is 0) || gap.overlapsWith(offset, length))) {
576 start= Math.max(offset, gapOffset);
577 end= Math.min(endOffset, fDocument.getLength());
578 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
579 }
580 }
581
582 if (list.isEmpty())
583 list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE));
584
585 } catch (BadPositionCategoryException x) {
586 }
587
588 TypedRegion[] result= new TypedRegion[list.size()];
589 list.toArray(result);
590 return result;
591 }
592 }