KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > rules > RuleBasedPartitioner


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  *******************************************************************************/

11
12 package org.eclipse.jface.text.rules;
13
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.List JavaDoc;
17
18 import org.eclipse.core.runtime.Assert;
19
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.BadPositionCategoryException;
22 import org.eclipse.jface.text.DefaultPositionUpdater;
23 import org.eclipse.jface.text.DocumentEvent;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IDocumentPartitioner;
26 import org.eclipse.jface.text.IDocumentPartitionerExtension;
27 import org.eclipse.jface.text.IDocumentPartitionerExtension2;
28 import org.eclipse.jface.text.IRegion;
29 import org.eclipse.jface.text.ITypedRegion;
30 import org.eclipse.jface.text.Position;
31 import org.eclipse.jface.text.Region;
32 import org.eclipse.jface.text.TextUtilities;
33 import org.eclipse.jface.text.TypedPosition;
34 import org.eclipse.jface.text.TypedRegion;
35
36
37
38 /**
39  * A standard implementation of a syntax driven document partitioner.
40  * It uses a rule based scanner to scan the document and to determine
41  * the document's partitioning. The tokens returned by the rules the
42  * scanner is configured with are supposed to return the partition type
43  * as their data. The partitioner remembers the document's partitions
44  * in the document itself rather than maintaining its own data structure.
45  *
46  * @see IRule
47  * @see RuleBasedScanner
48  *
49  * @deprecated use <code>FastPartitioner</code> instead
50  */

51 public class RuleBasedPartitioner implements IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2 {
52
53     /**
54      * The position category this partitioner uses to store the document's partitioning information
55      * @deprecated As of 3.0, use <code>getManagingPositionCategories()</code>.
56      */

57     public final static String JavaDoc CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$
58

59
60     /** The partitioner's scanner */
61     protected RuleBasedScanner fScanner;
62     /** The legal content types of this partitioner */
63     protected String JavaDoc[] fLegalContentTypes;
64     /** The partitioner's document */
65     protected IDocument fDocument;
66     /** The document length before a document change occurred */
67     protected int fPreviousDocumentLength;
68     /** The position updater used to for the default updating of partitions */
69     protected DefaultPositionUpdater fPositionUpdater;
70     /** The offset at which the first changed partition starts */
71     protected int fStartOffset;
72     /** The offset at which the last changed partition ends */
73     protected int fEndOffset;
74     /**The offset at which a partition has been deleted */
75     protected int fDeleteOffset;
76     /**
77      * The position category for managing partitioning information.
78      * @since 3.0
79      */

80     private String JavaDoc fPositionCategory;
81
82
83     /**
84      * Creates a new partitioner that uses the given scanner and may return
85      * partitions of the given legal content types.
86      *
87      * @param scanner the scanner this partitioner is supposed to use
88      * @param legalContentTypes the legal content types of this partitioner
89      */

90     public RuleBasedPartitioner(RuleBasedScanner scanner, String JavaDoc[] legalContentTypes) {
91         fScanner= scanner;
92         fLegalContentTypes= TextUtilities.copy(legalContentTypes);
93         fPositionCategory= CONTENT_TYPES_CATEGORY + hashCode();
94         fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
95     }
96
97     /*
98      * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories()
99      * @since 3.0
100      */

101     public String JavaDoc[] getManagingPositionCategories() {
102         return new String JavaDoc[] { fPositionCategory };
103     }
104
105     /*
106      * @see IDocumentPartitioner#connect
107      */

108     public void connect(IDocument document) {
109         Assert.isNotNull(document);
110         Assert.isTrue(!document.containsPositionCategory(fPositionCategory));
111
112         fDocument= document;
113         fDocument.addPositionCategory(fPositionCategory);
114
115         initialize();
116     }
117
118     /**
119      * Performs the initial partitioning of the partitioner's document.
120      */

121     protected void initialize() {
122
123         fScanner.setRange(fDocument, 0, fDocument.getLength());
124
125         try {
126             IToken token= fScanner.nextToken();
127             while (!token.isEOF()) {
128
129                 String JavaDoc contentType= getTokenContentType(token);
130
131                 if (isSupportedContentType(contentType)) {
132                     TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType);
133                     fDocument.addPosition(fPositionCategory, p);
134                 }
135
136                 token= fScanner.nextToken();
137             }
138         } catch (BadLocationException x) {
139             // cannot happen as offsets come from scanner
140
} catch (BadPositionCategoryException x) {
141             // cannot happen if document has been connected before
142
}
143     }
144
145     /*
146      * @see IDocumentPartitioner#disconnect
147      */

148     public void disconnect() {
149
150         Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory));
151
152         try {
153             fDocument.removePositionCategory(fPositionCategory);
154         } catch (BadPositionCategoryException x) {
155             // can not happen because of Assert
156
}
157     }
158
159     /*
160      * @see IDocumentPartitioner#documentAboutToBeChanged
161      */

162     public void documentAboutToBeChanged(DocumentEvent e) {
163
164         Assert.isTrue(e.getDocument() == fDocument);
165
166         fPreviousDocumentLength= e.getDocument().getLength();
167         fStartOffset= -1;
168         fEndOffset= -1;
169         fDeleteOffset= -1;
170     }
171
172     /*
173      * @see IDocumentPartitioner#documentChanged
174      */

175     public boolean documentChanged(DocumentEvent e) {
176         IRegion region= documentChanged2(e);
177         return (region != null);
178     }
179
180     /**
181      * Helper method for tracking the minimal region containing all partition changes.
182      * If <code>offset</code> is smaller than the remembered offset, <code>offset</code>
183      * will from now on be remembered. If <code>offset + length</code> is greater than
184      * the remembered end offset, it will be remembered from now on.
185      *
186      * @param offset the offset
187      * @param length the length
188      */

189     private void rememberRegion(int offset, int length) {
190         // remember start offset
191
if (fStartOffset == -1)
192             fStartOffset= offset;
193         else if (offset < fStartOffset)
194             fStartOffset= offset;
195
196         // remember end offset
197
int endOffset= offset + length;
198         if (fEndOffset == -1)
199             fEndOffset= endOffset;
200         else if (endOffset > fEndOffset)
201             fEndOffset= endOffset;
202     }
203
204     /**
205      * Remembers the given offset as the deletion offset.
206      *
207      * @param offset the offset
208      */

209     private void rememberDeletedOffset(int offset) {
210         fDeleteOffset= offset;
211     }
212
213     /**
214      * Creates the minimal region containing all partition changes using the
215      * remembered offset, end offset, and deletion offset.
216      * @return the minimal region containing all the partition changes
217      */

218     private IRegion createRegion() {
219         if (fDeleteOffset == -1) {
220             if (fStartOffset == -1 || fEndOffset == -1)
221                 return null;
222             return new Region(fStartOffset, fEndOffset - fStartOffset);
223         } else if (fStartOffset == -1 || fEndOffset == -1) {
224             return new Region(fDeleteOffset, 0);
225         } else {
226             int offset= Math.min(fDeleteOffset, fStartOffset);
227             int endOffset= Math.max(fDeleteOffset, fEndOffset);
228             return new Region(offset, endOffset - offset);
229         }
230     }
231
232     /*
233      * @see IDocumentPartitionerExtension#documentChanged2(DocumentEvent)
234      * @since 2.0
235      */

236     public IRegion documentChanged2(DocumentEvent e) {
237
238         try {
239
240             IDocument d= e.getDocument();
241             Position[] category= d.getPositions(fPositionCategory);
242             int first= 0;
243             int reparseStart= 0;
244             int originalSize= category.length;
245
246             if (originalSize > 0) {
247
248                 /*
249                  * determine character position at which the scanner starts:
250                  * first position behind the last non-default partition the actual position is not involved with
251                  */

252
253                 first= d.computeIndexInCategory(fPositionCategory, e.getOffset());
254
255                 Position p= null;
256                 do {
257                     --first;
258                     if (first < 0)
259                         break;
260
261                     p= category[first];
262
263                 } while (p.overlapsWith(e.getOffset(), e.getLength()) ||
264                             (e.getOffset() == fPreviousDocumentLength &&
265                              (p.getOffset() + p.getLength() == fPreviousDocumentLength)));
266
267                 fPositionUpdater.update(e);
268                 for (int i= 0; i < category.length; i++) {
269                     p= category[i];
270                     if (p.isDeleted) {
271                         rememberDeletedOffset(e.getOffset());
272                         break;
273                     }
274                 }
275                 category= d.getPositions(fPositionCategory);
276
277                 if (first >= 0) {
278                     p= category[first];
279                     reparseStart= p.getOffset() + p.getLength();
280                 }
281
282                 ++first;
283             }
284
285             fScanner.setRange(d, reparseStart, d.getLength() - reparseStart);
286
287             int lastScannedPosition= reparseStart;
288             IToken token= fScanner.nextToken();
289
290             while (!token.isEOF()) {
291
292
293                 String JavaDoc contentType= getTokenContentType(token);
294
295                 if (!isSupportedContentType(contentType)) {
296                     token= fScanner.nextToken();
297                     continue;
298                 }
299
300                 int start= fScanner.getTokenOffset();
301                 int length= fScanner.getTokenLength();
302
303                 lastScannedPosition= start + length - 1;
304
305                 // remove all affected positions
306
while (first < category.length) {
307                     TypedPosition p= (TypedPosition) category[first];
308                     if (lastScannedPosition >= p.offset + p.length ||
309                             (p.overlapsWith(start, length) &&
310                                 (!d.containsPosition(fPositionCategory, start, length) ||
311                                  !contentType.equals(p.getType())))) {
312
313                         rememberRegion(p.offset, p.length);
314                         d.removePosition(fPositionCategory, p);
315                         ++ first;
316
317                     } else
318                         break;
319                 }
320
321                 // if position already exists we are done
322
if (d.containsPosition(fPositionCategory, start, length))
323                     return createRegion();
324
325                 // insert the new type position
326
try {
327                     d.addPosition(fPositionCategory, new TypedPosition(start, length, contentType));
328                     rememberRegion(start, length);
329                 } catch (BadPositionCategoryException x) {
330                 } catch (BadLocationException x) {
331                 }
332
333                 token= fScanner.nextToken();
334             }
335
336
337             // remove all positions behind lastScannedPosition since there aren't any further types
338
if (lastScannedPosition != reparseStart) {
339                 // if this condition is not met, nothing has been scanned because of a delete
340
++ lastScannedPosition;
341             }
342             first= d.computeIndexInCategory(fPositionCategory, lastScannedPosition);
343
344             TypedPosition p;
345             while (first < category.length) {
346                 p= (TypedPosition) category[first++];
347                 d.removePosition(fPositionCategory, p);
348                 rememberRegion(p.offset, p.length);
349             }
350
351         } catch (BadPositionCategoryException x) {
352             // should never happen on connected documents
353
} catch (BadLocationException x) {
354         }
355
356         return createRegion();
357     }
358
359
360     /**
361      * Returns the position in the partitoner's position category which is
362      * close to the given offset. This is, the position has either an offset which
363      * is the same as the given offset or an offset which is smaller than the given
364      * offset. This method profits from the knowledge that a partitioning is
365      * a ordered set of disjoint position.
366      *
367      * @param offset the offset for which to search the closest position
368      * @return the closest position in the partitioner's category
369      */

370     protected TypedPosition findClosestPosition(int offset) {
371
372         try {
373
374             int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
375             Position[] category= fDocument.getPositions(fPositionCategory);
376
377             if (category.length == 0)
378                 return null;
379
380             if (index < category.length) {
381                 if (offset == category[index].offset)
382                     return (TypedPosition) category[index];
383             }
384
385             if (index > 0)
386                 index--;
387
388             return (TypedPosition) category[index];
389
390         } catch (BadPositionCategoryException x) {
391         } catch (BadLocationException x) {
392         }
393
394         return null;
395     }
396
397
398     /*
399      * @see IDocumentPartitioner#getContentType
400      */

401     public String JavaDoc getContentType(int offset) {
402
403         TypedPosition p= findClosestPosition(offset);
404         if (p != null && p.includes(offset))
405             return p.getType();
406
407         return IDocument.DEFAULT_CONTENT_TYPE;
408     }
409
410     /*
411      * @see IDocumentPartitioner#getPartition
412      */

413     public ITypedRegion getPartition(int offset) {
414
415         try {
416
417             Position[] category = fDocument.getPositions(fPositionCategory);
418
419             if (category == null || category.length == 0)
420                 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
421
422             int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
423
424             if (index < category.length) {
425
426                 TypedPosition next= (TypedPosition) category[index];
427
428                 if (offset == next.offset)
429                     return new TypedRegion(next.getOffset(), next.getLength(), next.getType());
430
431                 if (index == 0)
432                     return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE);
433
434                 TypedPosition previous= (TypedPosition) category[index - 1];
435                 if (previous.includes(offset))
436                     return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
437
438                 int endOffset= previous.getOffset() + previous.getLength();
439                 return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
440             }
441
442             TypedPosition previous= (TypedPosition) category[category.length - 1];
443             if (previous.includes(offset))
444                 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
445
446             int endOffset= previous.getOffset() + previous.getLength();
447             return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
448
449         } catch (BadPositionCategoryException x) {
450         } catch (BadLocationException x) {
451         }
452
453         return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
454     }
455
456     /*
457      * @see IDocumentPartitioner#computePartitioning
458      */

459     public ITypedRegion[] computePartitioning(int offset, int length) {
460         return computePartitioning(offset, length, false);
461     }
462
463     /*
464      * @see IDocumentPartitioner#getLegalContentTypes
465      */

466     public String JavaDoc[] getLegalContentTypes() {
467         return TextUtilities.copy(fLegalContentTypes);
468     }
469
470     /**
471      * Returns whether the given type is one of the legal content types.
472      *
473      * @param contentType the content type to check
474      * @return <code>true</code> if the content type is a legal content type
475      */

476     protected boolean isSupportedContentType(String JavaDoc contentType) {
477         if (contentType != null) {
478             for (int i= 0; i < fLegalContentTypes.length; i++) {
479                 if (fLegalContentTypes[i].equals(contentType))
480                     return true;
481             }
482         }
483
484         return false;
485     }
486
487     /**
488      * Returns a content type encoded in the given token. If the token's
489      * data is not <code>null</code> and a string it is assumed that
490      * it is the encoded content type.
491      *
492      * @param token the token whose content type is to be determined
493      * @return the token's content type
494      */

495     protected String JavaDoc getTokenContentType(IToken token) {
496         Object JavaDoc data= token.getData();
497         if (data instanceof String JavaDoc)
498             return (String JavaDoc) data;
499         return null;
500     }
501
502     /* zero-length partition support */
503
504     /*
505      * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getContentType(int)
506      * @since 3.0
507      */

508     public String JavaDoc getContentType(int offset, boolean preferOpenPartitions) {
509         return getPartition(offset, preferOpenPartitions).getType();
510     }
511
512     /*
513      * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#getPartition(int)
514      * @since 3.0
515      */

516     public ITypedRegion getPartition(int offset, boolean preferOpenPartitions) {
517         ITypedRegion region= getPartition(offset);
518         if (preferOpenPartitions) {
519             if (region.getOffset() == offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
520                 if (offset > 0) {
521                     region= getPartition(offset - 1);
522                     if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE))
523                         return region;
524                 }
525                 return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE);
526             }
527         }
528         return region;
529     }
530
531     /*
532      * @see org.eclipse.jface.text.IDocumentPartitionerExtension2#computePartitioning(int, int)
533      * @since 3.0
534      */

535     public ITypedRegion[] computePartitioning(int offset, int length, boolean includeZeroLengthPartitions) {
536         List JavaDoc list= new ArrayList JavaDoc();
537
538         try {
539
540             int endOffset= offset + length;
541
542             Position[] category= fDocument.getPositions(fPositionCategory);
543
544             TypedPosition previous= null, current= null;
545             int start, end, gapOffset;
546             Position gap= null;
547
548             for (int i= 0; i < category.length; i++) {
549
550                 current= (TypedPosition) category[i];
551
552                 gapOffset= (previous != null) ? previous.getOffset() + previous.getLength() : 0;
553                 gap= new Position(gapOffset, current.getOffset() - gapOffset);
554                 if ((includeZeroLengthPartitions || gap.getLength() > 0) && gap.overlapsWith(offset, length)) {
555                     start= Math.max(offset, gapOffset);
556                     end= Math.min(endOffset, gap.getOffset() + gap.getLength());
557                     list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
558                 }
559
560                 if (current.overlapsWith(offset, length)) {
561                     start= Math.max(offset, current.getOffset());
562                     end= Math.min(endOffset, current.getOffset() + current.getLength());
563                     list.add(new TypedRegion(start, end - start, current.getType()));
564                 }
565
566                 previous= current;
567             }
568
569             if (previous != null) {
570                 gapOffset= previous.getOffset() + previous.getLength();
571                 gap= new Position(gapOffset, fDocument.getLength() - gapOffset);
572                 if ((includeZeroLengthPartitions || gap.getLength() > 0) && ((includeZeroLengthPartitions && offset + length == gapOffset && gap.length == 0) || gap.overlapsWith(offset, length))) {
573                     start= Math.max(offset, gapOffset);
574                     end= Math.min(endOffset, fDocument.getLength());
575                     list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
576                 }
577             }
578
579             if (list.isEmpty())
580                 list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE));
581
582         } catch (BadPositionCategoryException x) {
583         }
584
585         TypedRegion[] result= new TypedRegion[list.size()];
586         list.toArray(result);
587         return result;
588     }
589 }
590
Popular Tags