KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > JavaHeuristicScanner


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 package org.eclipse.jdt.internal.ui.text;
12
13 import java.util.Arrays JavaDoc;
14
15 import org.eclipse.core.runtime.Assert;
16
17 import org.eclipse.jface.text.BadLocationException;
18 import org.eclipse.jface.text.IDocument;
19 import org.eclipse.jface.text.IRegion;
20 import org.eclipse.jface.text.ITypedRegion;
21 import org.eclipse.jface.text.Region;
22 import org.eclipse.jface.text.TextUtilities;
23 import org.eclipse.jface.text.TypedRegion;
24
25 import org.eclipse.jdt.ui.text.IJavaPartitions;
26
27 /**
28  * Utility methods for heuristic based Java manipulations in an incomplete Java source file.
29  *
30  * <p>An instance holds some internal position in the document and is therefore not threadsafe.</p>
31  *
32  * @since 3.0
33  */

34 public final class JavaHeuristicScanner implements Symbols {
35     /**
36      * Returned by all methods when the requested position could not be found, or if a
37      * {@link BadLocationException} was thrown while scanning.
38      */

39     public static final int NOT_FOUND= -1;
40
41     /**
42      * Special bound parameter that means either -1 (backward scanning) or
43      * <code>fDocument.getLength()</code> (forward scanning).
44      */

45     public static final int UNBOUND= -2;
46
47
48     /* character constants */
49     private static final char LBRACE= '{';
50     private static final char RBRACE= '}';
51     private static final char LPAREN= '(';
52     private static final char RPAREN= ')';
53     private static final char SEMICOLON= ';';
54     private static final char COLON= ':';
55     private static final char COMMA= ',';
56     private static final char LBRACKET= '[';
57     private static final char RBRACKET= ']';
58     private static final char QUESTIONMARK= '?';
59     private static final char EQUAL= '=';
60     private static final char LANGLE= '<';
61     private static final char RANGLE= '>';
62
63     /**
64      * Specifies the stop condition, upon which the <code>scanXXX</code> methods will decide whether
65      * to keep scanning or not. This interface may implemented by clients.
66      */

67     private static abstract class StopCondition {
68         /**
69          * Instructs the scanner to return the current position.
70          *
71          * @param ch the char at the current position
72          * @param position the current position
73          * @param forward the iteration direction
74          * @return <code>true</code> if the stop condition is met.
75          */

76         public abstract boolean stop(char ch, int position, boolean forward);
77
78         /**
79          * Asks the condition to return the next position to query. The default
80          * is to return the next/previous position.
81          *
82          * @return the next position to scan
83          */

84         public int nextPosition(int position, boolean forward) {
85             return forward ? position + 1 : position - 1;
86         }
87     }
88
89     /**
90      * Stops upon a non-whitespace (as defined by {@link Character#isWhitespace(char)}) character.
91      */

92     private static class NonWhitespace extends StopCondition {
93         /*
94          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#stop(char)
95          */

96         public boolean stop(char ch, int position, boolean forward) {
97             return !Character.isWhitespace(ch);
98         }
99     }
100
101     /**
102      * Stops upon a non-whitespace character in the default partition.
103      *
104      * @see JavaHeuristicScanner.NonWhitespace
105      */

106     private final class NonWhitespaceDefaultPartition extends NonWhitespace {
107         /*
108          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#stop(char)
109          */

110         public boolean stop(char ch, int position, boolean forward) {
111             return super.stop(ch, position, true) && isDefaultPartition(position);
112         }
113
114         /*
115          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#nextPosition(int, boolean)
116          */

117         public int nextPosition(int position, boolean forward) {
118             ITypedRegion partition= getPartition(position);
119             if (fPartition.equals(partition.getType()))
120                 return super.nextPosition(position, forward);
121
122             if (forward) {
123                 int end= partition.getOffset() + partition.getLength();
124                 if (position < end)
125                     return end;
126             } else {
127                 int offset= partition.getOffset();
128                 if (position > offset)
129                     return offset - 1;
130             }
131             return super.nextPosition(position, forward);
132         }
133     }
134
135     /**
136      * Stops upon a non-java identifier (as defined by {@link Character#isJavaIdentifierPart(char)}) character.
137      */

138     private static class NonJavaIdentifierPart extends StopCondition {
139         /*
140          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#stop(char)
141          */

142         public boolean stop(char ch, int position, boolean forward) {
143             return !Character.isJavaIdentifierPart(ch);
144         }
145     }
146
147     /**
148      * Stops upon a non-java identifier character in the default partition.
149      *
150      * @see JavaHeuristicScanner.NonJavaIdentifierPart
151      */

152     private final class NonJavaIdentifierPartDefaultPartition extends NonJavaIdentifierPart {
153         /*
154          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#stop(char)
155          */

156         public boolean stop(char ch, int position, boolean forward) {
157             return super.stop(ch, position, true) || !isDefaultPartition(position);
158         }
159
160         /*
161          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#nextPosition(int, boolean)
162          */

163         public int nextPosition(int position, boolean forward) {
164             ITypedRegion partition= getPartition(position);
165             if (fPartition.equals(partition.getType()))
166                 return super.nextPosition(position, forward);
167
168             if (forward) {
169                 int end= partition.getOffset() + partition.getLength();
170                 if (position < end)
171                     return end;
172             } else {
173                 int offset= partition.getOffset();
174                 if (position > offset)
175                     return offset - 1;
176             }
177             return super.nextPosition(position, forward);
178         }
179     }
180
181     /**
182      * Stops upon a character in the default partition that matches the given character list.
183      */

184     private final class CharacterMatch extends StopCondition {
185         private final char[] fChars;
186
187         /**
188          * Creates a new instance.
189          * @param ch the single character to match
190          */

191         public CharacterMatch(char ch) {
192             this(new char[] {ch});
193         }
194
195         /**
196          * Creates a new instance.
197          * @param chars the chars to match.
198          */

199         public CharacterMatch(char[] chars) {
200             Assert.isNotNull(chars);
201             Assert.isTrue(chars.length > 0);
202             fChars= chars;
203             Arrays.sort(chars);
204         }
205
206         /*
207          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#stop(char, int)
208          */

209         public boolean stop(char ch, int position, boolean forward) {
210             return Arrays.binarySearch(fChars, ch) >= 0 && isDefaultPartition(position);
211         }
212
213         /*
214          * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner.StopCondition#nextPosition(int, boolean)
215          */

216         public int nextPosition(int position, boolean forward) {
217             ITypedRegion partition= getPartition(position);
218             if (fPartition.equals(partition.getType()))
219                 return super.nextPosition(position, forward);
220
221             if (forward) {
222                 int end= partition.getOffset() + partition.getLength();
223                 if (position < end)
224                     return end;
225             } else {
226                 int offset= partition.getOffset();
227                 if (position > offset)
228                     return offset - 1;
229             }
230             return super.nextPosition(position, forward);
231         }
232     }
233
234     /** The document being scanned. */
235     private final IDocument fDocument;
236     /** The partitioning being used for scanning. */
237     private final String JavaDoc fPartitioning;
238     /** The partition to scan in. */
239     private final String JavaDoc fPartition;
240
241     /* internal scan state */
242
243     /** the most recently read character. */
244     private char fChar;
245     /** the most recently read position. */
246     private int fPos;
247     /**
248      * The most recently used partition.
249      * @since 3.2
250      */

251     private ITypedRegion fCachedPartition= new TypedRegion(-1, 0, "__no_partition_at_all"); //$NON-NLS-1$
252

253     /* preset stop conditions */
254     private final StopCondition fNonWSDefaultPart= new NonWhitespaceDefaultPartition();
255     private final static StopCondition fNonWS= new NonWhitespace();
256     private final StopCondition fNonIdent= new NonJavaIdentifierPartDefaultPartition();
257
258     /**
259      * Creates a new instance.
260      *
261      * @param document the document to scan
262      * @param partitioning the partitioning to use for scanning
263      * @param partition the partition to scan in
264      */

265     public JavaHeuristicScanner(IDocument document, String JavaDoc partitioning, String JavaDoc partition) {
266         Assert.isLegal(document != null);
267         Assert.isLegal(partitioning != null);
268         Assert.isLegal(partition != null);
269         fDocument= document;
270         fPartitioning= partitioning;
271         fPartition= partition;
272     }
273
274     /**
275      * Calls <code>this(document, IJavaPartitions.JAVA_PARTITIONING, IDocument.DEFAULT_CONTENT_TYPE)</code>.
276      *
277      * @param document the document to scan.
278      */

279     public JavaHeuristicScanner(IDocument document) {
280         this(document, IJavaPartitions.JAVA_PARTITIONING, IDocument.DEFAULT_CONTENT_TYPE);
281     }
282
283     /**
284      * Returns the most recent internal scan position.
285      *
286      * @return the most recent internal scan position.
287      */

288     public int getPosition() {
289         return fPos;
290     }
291
292     /**
293      * Returns the next token in forward direction, starting at <code>start</code>, and not extending
294      * further than <code>bound</code>. The return value is one of the constants defined in {@link Symbols}.
295      * After a call, {@link #getPosition()} will return the position just after the scanned token
296      * (i.e. the next position that will be scanned).
297      *
298      * @param start the first character position in the document to consider
299      * @param bound the first position not to consider any more
300      * @return a constant from {@link Symbols} describing the next token
301      */

302     public int nextToken(int start, int bound) {
303         int pos= scanForward(start, bound, fNonWSDefaultPart);
304         if (pos == NOT_FOUND)
305             return TokenEOF;
306
307         fPos++;
308
309         switch (fChar) {
310             case LBRACE:
311                 return TokenLBRACE;
312             case RBRACE:
313                 return TokenRBRACE;
314             case LBRACKET:
315                 return TokenLBRACKET;
316             case RBRACKET:
317                 return TokenRBRACKET;
318             case LPAREN:
319                 return TokenLPAREN;
320             case RPAREN:
321                 return TokenRPAREN;
322             case SEMICOLON:
323                 return TokenSEMICOLON;
324             case COMMA:
325                 return TokenCOMMA;
326             case QUESTIONMARK:
327                 return TokenQUESTIONMARK;
328             case EQUAL:
329                 return TokenEQUAL;
330             case LANGLE:
331                 return TokenLESSTHAN;
332             case RANGLE:
333                 return TokenGREATERTHAN;
334         }
335
336         // else
337
if (Character.isJavaIdentifierPart(fChar)) {
338             // assume an identifier or keyword
339
int from= pos, to;
340             pos= scanForward(pos + 1, bound, fNonIdent);
341             if (pos == NOT_FOUND)
342                 to= bound == UNBOUND ? fDocument.getLength() : bound;
343             else
344                 to= pos;
345
346             String JavaDoc identOrKeyword;
347             try {
348                 identOrKeyword= fDocument.get(from, to - from);
349             } catch (BadLocationException e) {
350                 return TokenEOF;
351             }
352
353             return getToken(identOrKeyword);
354
355
356         } else {
357             // operators, number literals etc
358
return TokenOTHER;
359         }
360     }
361
362     /**
363      * Returns the next token in backward direction, starting at <code>start</code>, and not extending
364      * further than <code>bound</code>. The return value is one of the constants defined in {@link Symbols}.
365      * After a call, {@link #getPosition()} will return the position just before the scanned token
366      * starts (i.e. the next position that will be scanned).
367      *
368      * @param start the first character position in the document to consider
369      * @param bound the first position not to consider any more
370      * @return a constant from {@link Symbols} describing the previous token
371      */

372     public int previousToken(int start, int bound) {
373         int pos= scanBackward(start, bound, fNonWSDefaultPart);
374         if (pos == NOT_FOUND)
375             return TokenEOF;
376
377         fPos--;
378
379         switch (fChar) {
380             case LBRACE:
381                 return TokenLBRACE;
382             case RBRACE:
383                 return TokenRBRACE;
384             case LBRACKET:
385                 return TokenLBRACKET;
386             case RBRACKET:
387                 return TokenRBRACKET;
388             case LPAREN:
389                 return TokenLPAREN;
390             case RPAREN:
391                 return TokenRPAREN;
392             case SEMICOLON:
393                 return TokenSEMICOLON;
394             case COLON:
395                 return TokenCOLON;
396             case COMMA:
397                 return TokenCOMMA;
398             case QUESTIONMARK:
399                 return TokenQUESTIONMARK;
400             case EQUAL:
401                 return TokenEQUAL;
402             case LANGLE:
403                 return TokenLESSTHAN;
404             case RANGLE:
405                 return TokenGREATERTHAN;
406         }
407
408         // else
409
if (Character.isJavaIdentifierPart(fChar)) {
410             // assume an ident or keyword
411
int from, to= pos + 1;
412             pos= scanBackward(pos - 1, bound, fNonIdent);
413             if (pos == NOT_FOUND)
414                 from= bound == UNBOUND ? 0 : bound + 1;
415             else
416                 from= pos + 1;
417
418             String JavaDoc identOrKeyword;
419             try {
420                 identOrKeyword= fDocument.get(from, to - from);
421             } catch (BadLocationException e) {
422                 return TokenEOF;
423             }
424
425             return getToken(identOrKeyword);
426
427
428         } else {
429             // operators, number literals etc
430
return TokenOTHER;
431         }
432
433     }
434
435     /**
436      * Returns one of the keyword constants or <code>TokenIDENT</code> for a scanned identifier.
437      *
438      * @param s a scanned identifier
439      * @return one of the constants defined in {@link Symbols}
440      */

441     private int getToken(String JavaDoc s) {
442         Assert.isNotNull(s);
443
444         switch (s.length()) {
445             case 2:
446                 if ("if".equals(s)) //$NON-NLS-1$
447
return TokenIF;
448                 if ("do".equals(s)) //$NON-NLS-1$
449
return TokenDO;
450                 break;
451             case 3:
452                 if ("for".equals(s)) //$NON-NLS-1$
453
return TokenFOR;
454                 if ("try".equals(s)) //$NON-NLS-1$
455
return TokenTRY;
456                 if ("new".equals(s)) //$NON-NLS-1$
457
return TokenNEW;
458                 break;
459             case 4:
460                 if ("case".equals(s)) //$NON-NLS-1$
461
return TokenCASE;
462                 if ("else".equals(s)) //$NON-NLS-1$
463
return TokenELSE;
464                 if ("enum".equals(s)) //$NON-NLS-1$
465
return TokenENUM;
466                 if ("goto".equals(s)) //$NON-NLS-1$
467
return TokenGOTO;
468                 break;
469             case 5:
470                 if ("break".equals(s)) //$NON-NLS-1$
471
return TokenBREAK;
472                 if ("catch".equals(s)) //$NON-NLS-1$
473
return TokenCATCH;
474                 if ("class".equals(s)) //$NON-NLS-1$
475
return TokenCLASS;
476                 if ("while".equals(s)) //$NON-NLS-1$
477
return TokenWHILE;
478                 break;
479             case 6:
480                 if ("return".equals(s)) //$NON-NLS-1$
481
return TokenRETURN;
482                 if ("static".equals(s)) //$NON-NLS-1$
483
return TokenSTATIC;
484                 if ("switch".equals(s)) //$NON-NLS-1$
485
return TokenSWITCH;
486                 break;
487             case 7:
488                 if ("default".equals(s)) //$NON-NLS-1$
489
return TokenDEFAULT;
490                 if ("finally".equals(s)) //$NON-NLS-1$
491
return TokenFINALLY;
492                 break;
493             case 9:
494                 if ("interface".equals(s)) //$NON-NLS-1$
495
return TokenINTERFACE;
496                 break;
497             case 12:
498                 if ("synchronized".equals(s)) //$NON-NLS-1$
499
return TokenSYNCHRONIZED;
500                 break;
501         }
502         return TokenIDENT;
503     }
504
505     /**
506      * Returns the position of the closing peer character (forward search). Any scopes introduced by opening peers
507      * are skipped. All peers accounted for must reside in the default partition.
508      *
509      * <p>Note that <code>start</code> must not point to the opening peer, but to the first
510      * character being searched.</p>
511      *
512      * @param start the start position
513      * @param openingPeer the opening peer character (e.g. '{')
514      * @param closingPeer the closing peer character (e.g. '}')
515      * @return the matching peer character position, or <code>NOT_FOUND</code>
516      */

517     public int findClosingPeer(int start, final char openingPeer, final char closingPeer) {
518         return findClosingPeer(start, UNBOUND, openingPeer, closingPeer);
519     }
520
521     /**
522      * Returns the position of the closing peer character (forward search). Any scopes introduced by opening peers
523      * are skipped. All peers accounted for must reside in the default partition.
524      *
525      * <p>Note that <code>start</code> must not point to the opening peer, but to the first
526      * character being searched.</p>
527      *
528      * @param start the start position
529      * @param bound the bound
530      * @param openingPeer the opening peer character (e.g. '{')
531      * @param closingPeer the closing peer character (e.g. '}')
532      * @return the matching peer character position, or <code>NOT_FOUND</code>
533      */

534     public int findClosingPeer(int start, int bound, final char openingPeer, final char closingPeer) {
535         Assert.isLegal(start >= 0);
536
537         try {
538             CharacterMatch match= new CharacterMatch(new char[] {openingPeer, closingPeer});
539             int depth= 1;
540             start -= 1;
541             while (true) {
542                 start= scanForward(start + 1, bound, match);
543                 if (start == NOT_FOUND)
544                     return NOT_FOUND;
545
546                 if (fDocument.getChar(start) == openingPeer)
547                     depth++;
548                 else
549                     depth--;
550
551                 if (depth == 0)
552                     return start;
553             }
554
555         } catch (BadLocationException e) {
556             return NOT_FOUND;
557         }
558     }
559
560     /**
561      * Returns the position of the opening peer character (backward search). Any scopes introduced by closing peers
562      * are skipped. All peers accounted for must reside in the default partition.
563      *
564      * <p>Note that <code>start</code> must not point to the closing peer, but to the first
565      * character being searched.</p>
566      *
567      * @param start the start position
568      * @param openingPeer the opening peer character (e.g. '{')
569      * @param closingPeer the closing peer character (e.g. '}')
570      * @return the matching peer character position, or <code>NOT_FOUND</code>
571      */

572     public int findOpeningPeer(int start, char openingPeer, char closingPeer) {
573         return findOpeningPeer(start, UNBOUND, openingPeer, closingPeer);
574     }
575
576     /**
577      * Returns the position of the opening peer character (backward search). Any scopes introduced by closing peers
578      * are skipped. All peers accounted for must reside in the default partition.
579      *
580      * <p>Note that <code>start</code> must not point to the closing peer, but to the first
581      * character being searched.</p>
582      *
583      * @param start the start position
584      * @param bound the bound
585      * @param openingPeer the opening peer character (e.g. '{')
586      * @param closingPeer the closing peer character (e.g. '}')
587      * @return the matching peer character position, or <code>NOT_FOUND</code>
588      */

589     public int findOpeningPeer(int start, int bound, char openingPeer, char closingPeer) {
590         Assert.isLegal(start < fDocument.getLength());
591
592         try {
593             final CharacterMatch match= new CharacterMatch(new char[] {openingPeer, closingPeer});
594             int depth= 1;
595             start += 1;
596             while (true) {
597                 start= scanBackward(start - 1, bound, match);
598                 if (start == NOT_FOUND)
599                     return NOT_FOUND;
600
601                 if (fDocument.getChar(start) == closingPeer)
602                     depth++;
603                 else
604                     depth--;
605
606                 if (depth == 0)
607                     return start;
608             }
609
610         } catch (BadLocationException e) {
611             return NOT_FOUND;
612         }
613     }
614
615     /**
616      * Computes the surrounding block around <code>offset</code>. The search is started at the
617      * beginning of <code>offset</code>, i.e. an opening brace at <code>offset</code> will not be
618      * part of the surrounding block, but a closing brace will.
619      *
620      * @param offset the offset for which the surrounding block is computed
621      * @return a region describing the surrounding block, or <code>null</code> if none can be found
622      */

623     public IRegion findSurroundingBlock(int offset) {
624         if (offset < 1 || offset >= fDocument.getLength())
625             return null;
626
627         int begin= findOpeningPeer(offset - 1, LBRACE, RBRACE);
628         int end= findClosingPeer(offset, LBRACE, RBRACE);
629         if (begin == NOT_FOUND || end == NOT_FOUND)
630             return null;
631         return new Region(begin, end + 1 - begin);
632     }
633
634     /**
635      * Finds the smallest position in <code>fDocument</code> such that the position is &gt;= <code>position</code>
636      * and &lt; <code>bound</code> and <code>Character.isWhitespace(fDocument.getChar(pos))</code> evaluates to <code>false</code>
637      * and the position is in the default partition.
638      *
639      * @param position the first character position in <code>fDocument</code> to be considered
640      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &gt; <code>position</code>, or <code>UNBOUND</code>
641      * @return the smallest position of a non-whitespace character in [<code>position</code>, <code>bound</code>) that resides in a Java partition, or <code>NOT_FOUND</code> if none can be found
642      */

643     public int findNonWhitespaceForward(int position, int bound) {
644         return scanForward(position, bound, fNonWSDefaultPart);
645     }
646
647     /**
648      * Finds the smallest position in <code>fDocument</code> such that the position is &gt;= <code>position</code>
649      * and &lt; <code>bound</code> and <code>Character.isWhitespace(fDocument.getChar(pos))</code> evaluates to <code>false</code>.
650      *
651      * @param position the first character position in <code>fDocument</code> to be considered
652      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &gt; <code>position</code>, or <code>UNBOUND</code>
653      * @return the smallest position of a non-whitespace character in [<code>position</code>, <code>bound</code>), or <code>NOT_FOUND</code> if none can be found
654      */

655     public int findNonWhitespaceForwardInAnyPartition(int position, int bound) {
656         return scanForward(position, bound, fNonWS);
657     }
658
659     /**
660      * Finds the highest position in <code>fDocument</code> such that the position is &lt;= <code>position</code>
661      * and &gt; <code>bound</code> and <code>Character.isWhitespace(fDocument.getChar(pos))</code> evaluates to <code>false</code>
662      * and the position is in the default partition.
663      *
664      * @param position the first character position in <code>fDocument</code> to be considered
665      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &lt; <code>position</code>, or <code>UNBOUND</code>
666      * @return the highest position of a non-whitespace character in (<code>bound</code>, <code>position</code>] that resides in a Java partition, or <code>NOT_FOUND</code> if none can be found
667      */

668     public int findNonWhitespaceBackward(int position, int bound) {
669         return scanBackward(position, bound, fNonWSDefaultPart);
670     }
671
672     /**
673      * Finds the lowest position <code>p</code> in <code>fDocument</code> such that <code>start</code> &lt;= p &lt;
674      * <code>bound</code> and <code>condition.stop(fDocument.getChar(p), p)</code> evaluates to <code>true</code>.
675      *
676      * @param start the first character position in <code>fDocument</code> to be considered
677      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &gt; <code>start</code>, or <code>UNBOUND</code>
678      * @param condition the <code>StopCondition</code> to check
679      * @return the lowest position in [<code>start</code>, <code>bound</code>) for which <code>condition</code> holds, or <code>NOT_FOUND</code> if none can be found
680      */

681     public int scanForward(int start, int bound, StopCondition condition) {
682         Assert.isLegal(start >= 0);
683
684         if (bound == UNBOUND)
685             bound= fDocument.getLength();
686
687         Assert.isLegal(bound <= fDocument.getLength());
688
689         try {
690             fPos= start;
691             while (fPos < bound) {
692
693                 fChar= fDocument.getChar(fPos);
694                 if (condition.stop(fChar, fPos, true))
695                     return fPos;
696
697                 fPos= condition.nextPosition(fPos, true);
698             }
699         } catch (BadLocationException e) {
700         }
701         return NOT_FOUND;
702     }
703
704
705     /**
706      * Finds the lowest position in <code>fDocument</code> such that the position is &gt;= <code>position</code>
707      * and &lt; <code>bound</code> and <code>fDocument.getChar(position) == ch</code> evaluates to <code>true</code>
708      * and the position is in the default partition.
709      *
710      * @param position the first character position in <code>fDocument</code> to be considered
711      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &gt; <code>position</code>, or <code>UNBOUND</code>
712      * @param ch the <code>char</code> to search for
713      * @return the lowest position of <code>ch</code> in (<code>bound</code>, <code>position</code>] that resides in a Java partition, or <code>NOT_FOUND</code> if none can be found
714      */

715     public int scanForward(int position, int bound, char ch) {
716         return scanForward(position, bound, new CharacterMatch(ch));
717     }
718
719     /**
720      * Finds the lowest position in <code>fDocument</code> such that the position is &gt;= <code>position</code>
721      * and &lt; <code>bound</code> and <code>fDocument.getChar(position) == ch</code> evaluates to <code>true</code> for at least one
722      * ch in <code>chars</code> and the position is in the default partition.
723      *
724      * @param position the first character position in <code>fDocument</code> to be considered
725      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &gt; <code>position</code>, or <code>UNBOUND</code>
726      * @param chars an array of <code>char</code> to search for
727      * @return the lowest position of a non-whitespace character in [<code>position</code>, <code>bound</code>) that resides in a Java partition, or <code>NOT_FOUND</code> if none can be found
728      */

729     public int scanForward(int position, int bound, char[] chars) {
730         return scanForward(position, bound, new CharacterMatch(chars));
731     }
732
733     /**
734      * Finds the highest position <code>p</code> in <code>fDocument</code> such that <code>bound</code> &lt; <code>p</code> &lt;= <code>start</code>
735      * and <code>condition.stop(fDocument.getChar(p), p)</code> evaluates to <code>true</code>.
736      *
737      * @param start the first character position in <code>fDocument</code> to be considered
738      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &lt; <code>start</code>, or <code>UNBOUND</code>
739      * @param condition the <code>StopCondition</code> to check
740      * @return the highest position in (<code>bound</code>, <code>start</code> for which <code>condition</code> holds, or <code>NOT_FOUND</code> if none can be found
741      */

742     public int scanBackward(int start, int bound, StopCondition condition) {
743         if (bound == UNBOUND)
744             bound= -1;
745
746         Assert.isLegal(bound >= -1);
747         Assert.isLegal(start < fDocument.getLength() );
748
749         try {
750             fPos= start;
751             while (fPos > bound) {
752
753                 fChar= fDocument.getChar(fPos);
754                 if (condition.stop(fChar, fPos, false))
755                     return fPos;
756
757                 fPos= condition.nextPosition(fPos, false);
758             }
759         } catch (BadLocationException e) {
760         }
761         return NOT_FOUND;
762     }
763
764     /**
765      * Finds the highest position in <code>fDocument</code> such that the position is &lt;= <code>position</code>
766      * and &gt; <code>bound</code> and <code>fDocument.getChar(position) == ch</code> evaluates to <code>true</code> for at least one
767      * ch in <code>chars</code> and the position is in the default partition.
768      *
769      * @param position the first character position in <code>fDocument</code> to be considered
770      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &lt; <code>position</code>, or <code>UNBOUND</code>
771      * @param ch the <code>char</code> to search for
772      * @return the highest position of one element in <code>chars</code> in (<code>bound</code>, <code>position</code>] that resides in a Java partition, or <code>NOT_FOUND</code> if none can be found
773      */

774     public int scanBackward(int position, int bound, char ch) {
775         return scanBackward(position, bound, new CharacterMatch(ch));
776     }
777
778     /**
779      * Finds the highest position in <code>fDocument</code> such that the position is &lt;= <code>position</code>
780      * and &gt; <code>bound</code> and <code>fDocument.getChar(position) == ch</code> evaluates to <code>true</code> for at least one
781      * ch in <code>chars</code> and the position is in the default partition.
782      *
783      * @param position the first character position in <code>fDocument</code> to be considered
784      * @param bound the first position in <code>fDocument</code> to not consider any more, with <code>bound</code> &lt; <code>position</code>, or <code>UNBOUND</code>
785      * @param chars an array of <code>char</code> to search for
786      * @return the highest position of one element in <code>chars</code> in (<code>bound</code>, <code>position</code>] that resides in a Java partition, or <code>NOT_FOUND</code> if none can be found
787      */

788     public int scanBackward(int position, int bound, char[] chars) {
789         return scanBackward(position, bound, new CharacterMatch(chars));
790     }
791
792     /**
793      * Checks whether <code>position</code> resides in a default (Java) partition of <code>fDocument</code>.
794      *
795      * @param position the position to be checked
796      * @return <code>true</code> if <code>position</code> is in the default partition of <code>fDocument</code>, <code>false</code> otherwise
797      */

798     public boolean isDefaultPartition(int position) {
799         return fPartition.equals(getPartition(position).getType());
800     }
801
802     /**
803      * Returns the partition at <code>position</code>.
804      *
805      * @param position the position to get the partition for
806      * @return the partition at <code>position</code> or a dummy zero-length
807      * partition if accessing the document fails
808      */

809     private ITypedRegion getPartition(int position) {
810         if (!contains(fCachedPartition, position)) {
811             Assert.isTrue(position >= 0);
812             Assert.isTrue(position <= fDocument.getLength());
813             
814             try {
815                 fCachedPartition= TextUtilities.getPartition(fDocument, fPartitioning, position, false);
816             } catch (BadLocationException e) {
817                 fCachedPartition= new TypedRegion(position, 0, "__no_partition_at_all"); //$NON-NLS-1$
818
}
819         }
820
821         return fCachedPartition;
822     }
823
824     /**
825      * Returns <code>true</code> if <code>region</code> contains <code>position</code>.
826      *
827      * @param region a region
828      * @param position an offset
829      * @return <code>true</code> if <code>region</code> contains <code>position</code>
830      * @since 3.2
831      */

832     private boolean contains(IRegion region, int position) {
833         int offset= region.getOffset();
834         return offset <= position && position < offset + region.getLength();
835     }
836
837     /**
838      * Checks if the line seems to be an open condition not followed by a block (i.e. an if, while,
839      * or for statement with just one following statement, see example below).
840      *
841      * <pre>
842      * if (condition)
843      * doStuff();
844      * </pre>
845      *
846      * <p>Algorithm: if the last non-WS, non-Comment code on the line is an if (condition), while (condition),
847      * for( expression), do, else, and there is no statement after that </p>
848      *
849      * @param position the insert position of the new character
850      * @param bound the lowest position to consider
851      * @return <code>true</code> if the code is a conditional statement or loop without a block, <code>false</code> otherwise
852      */

853     public boolean isBracelessBlockStart(int position, int bound) {
854         if (position < 1)
855             return false;
856
857         switch (previousToken(position, bound)) {
858             case TokenDO:
859             case TokenELSE:
860                 return true;
861             case TokenRPAREN:
862                 position= findOpeningPeer(fPos, LPAREN, RPAREN);
863                 if (position > 0) {
864                     switch (previousToken(position - 1, bound)) {
865                         case TokenIF:
866                         case TokenFOR:
867                         case TokenWHILE:
868                             return true;
869                     }
870                 }
871         }
872
873         return false;
874     }
875     
876     /**
877      * Returns <code>true</code> if the document, when scanned backwards from <code>start</code>
878      * appears to contain a class instance creation, i.e. a possibly qualified name preceded by a
879      * <code>new</code> keyword. The <code>start</code> must be at the end of the type name, and
880      * before any generic signature or constructor parameter list. The heuristic will return
881      * <code>true</code> if <code>start</code> is at the following positions (|):
882      *
883      * <pre>
884      * new java.util. ArrayList|&lt;String&gt;(10)
885      * new ArrayList |(10)
886      * new / * comment * / ArrayList |(10)
887      * </pre>
888      *
889      * but not the following:
890      *
891      * <pre>
892      * new java.util. ArrayList&lt;String&gt;(10)|
893      * new java.util. ArrayList&lt;String&gt;|(10)
894      * new ArrayList (10)|
895      * ArrayList |(10)
896      * </pre>
897      *
898      * @param start the position where the type name of the class instance creation supposedly ends
899      * @param bound the first position in <code>fDocument</code> to not consider any more, with
900      * <code>bound</code> &lt; <code>start</code>, or <code>UNBOUND</code>
901      * @return <code>true</code> if the current position looks like after the type name of a class
902      * instance creation
903      * @since 3.2
904      */

905     public boolean looksLikeClassInstanceCreationBackward(int start, int bound) {
906         int token= previousToken(start - 1, bound);
907         if (token == Symbols.TokenIDENT) { // type name
908
token= previousToken(getPosition(), bound);
909             while (token == Symbols.TokenOTHER) { // dot of qualification
910
token= previousToken(getPosition(), bound);
911                 if (token != Symbols.TokenIDENT) // qualification name
912
return false;
913                 token= previousToken(getPosition(), bound);
914             }
915             return token == Symbols.TokenNEW;
916         }
917         return false;
918     }
919
920     /**
921      * Returns <code>true</code> if <code>identifier</code> is probably a
922      * type variable or type name, <code>false</code> if it is rather not.
923      * This is a heuristic.
924      *
925      * @param identifier the identifier to check
926      * @return <code>true</code> if <code>identifier</code> is probably a
927      * type variable or type name, <code>false</code> if not
928      * @since 3.2
929      */

930     public static boolean isGenericStarter(CharSequence JavaDoc identifier) {
931         /* This heuristic allows any identifiers if they start with an upper
932          * case. This will fail when a comparison is made with constants:
933          *
934          * if (MAX > foo)
935          *
936          * will try to find the matching '<' which will never come
937          *
938          * Also, it will fail on lower case types and type variables
939          */

940         int length= identifier.length();
941         if (length > 0 && Character.isUpperCase(identifier.charAt(0))) {
942             for (int i= 0; i < length; i++) {
943                 if (identifier.charAt(i) == '_')
944                     return false;
945             }
946             return true;
947         }
948         return false;
949     }
950
951
952 }
953
Popular Tags