1 11 package org.eclipse.jdt.internal.ui.text; 12 13 import java.util.Arrays ; 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 34 public final class JavaHeuristicScanner implements Symbols { 35 39 public static final int NOT_FOUND= -1; 40 41 45 public static final int UNBOUND= -2; 46 47 48 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 67 private static abstract class StopCondition { 68 76 public abstract boolean stop(char ch, int position, boolean forward); 77 78 84 public int nextPosition(int position, boolean forward) { 85 return forward ? position + 1 : position - 1; 86 } 87 } 88 89 92 private static class NonWhitespace extends StopCondition { 93 96 public boolean stop(char ch, int position, boolean forward) { 97 return !Character.isWhitespace(ch); 98 } 99 } 100 101 106 private final class NonWhitespaceDefaultPartition extends NonWhitespace { 107 110 public boolean stop(char ch, int position, boolean forward) { 111 return super.stop(ch, position, true) && isDefaultPartition(position); 112 } 113 114 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 138 private static class NonJavaIdentifierPart extends StopCondition { 139 142 public boolean stop(char ch, int position, boolean forward) { 143 return !Character.isJavaIdentifierPart(ch); 144 } 145 } 146 147 152 private final class NonJavaIdentifierPartDefaultPartition extends NonJavaIdentifierPart { 153 156 public boolean stop(char ch, int position, boolean forward) { 157 return super.stop(ch, position, true) || !isDefaultPartition(position); 158 } 159 160 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 184 private final class CharacterMatch extends StopCondition { 185 private final char[] fChars; 186 187 191 public CharacterMatch(char ch) { 192 this(new char[] {ch}); 193 } 194 195 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 209 public boolean stop(char ch, int position, boolean forward) { 210 return Arrays.binarySearch(fChars, ch) >= 0 && isDefaultPartition(position); 211 } 212 213 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 235 private final IDocument fDocument; 236 237 private final String fPartitioning; 238 239 private final String fPartition; 240 241 242 243 244 private char fChar; 245 246 private int fPos; 247 251 private ITypedRegion fCachedPartition= new TypedRegion(-1, 0, "__no_partition_at_all"); 253 254 private final StopCondition fNonWSDefaultPart= new NonWhitespaceDefaultPartition(); 255 private final static StopCondition fNonWS= new NonWhitespace(); 256 private final StopCondition fNonIdent= new NonJavaIdentifierPartDefaultPartition(); 257 258 265 public JavaHeuristicScanner(IDocument document, String partitioning, String 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 279 public JavaHeuristicScanner(IDocument document) { 280 this(document, IJavaPartitions.JAVA_PARTITIONING, IDocument.DEFAULT_CONTENT_TYPE); 281 } 282 283 288 public int getPosition() { 289 return fPos; 290 } 291 292 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 if (Character.isJavaIdentifierPart(fChar)) { 338 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 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 return TokenOTHER; 359 } 360 } 361 362 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 if (Character.isJavaIdentifierPart(fChar)) { 410 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 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 return TokenOTHER; 431 } 432 433 } 434 435 441 private int getToken(String s) { 442 Assert.isNotNull(s); 443 444 switch (s.length()) { 445 case 2: 446 if ("if".equals(s)) return TokenIF; 448 if ("do".equals(s)) return TokenDO; 450 break; 451 case 3: 452 if ("for".equals(s)) return TokenFOR; 454 if ("try".equals(s)) return TokenTRY; 456 if ("new".equals(s)) return TokenNEW; 458 break; 459 case 4: 460 if ("case".equals(s)) return TokenCASE; 462 if ("else".equals(s)) return TokenELSE; 464 if ("enum".equals(s)) return TokenENUM; 466 if ("goto".equals(s)) return TokenGOTO; 468 break; 469 case 5: 470 if ("break".equals(s)) return TokenBREAK; 472 if ("catch".equals(s)) return TokenCATCH; 474 if ("class".equals(s)) return TokenCLASS; 476 if ("while".equals(s)) return TokenWHILE; 478 break; 479 case 6: 480 if ("return".equals(s)) return TokenRETURN; 482 if ("static".equals(s)) return TokenSTATIC; 484 if ("switch".equals(s)) return TokenSWITCH; 486 break; 487 case 7: 488 if ("default".equals(s)) return TokenDEFAULT; 490 if ("finally".equals(s)) return TokenFINALLY; 492 break; 493 case 9: 494 if ("interface".equals(s)) return TokenINTERFACE; 496 break; 497 case 12: 498 if ("synchronized".equals(s)) return TokenSYNCHRONIZED; 500 break; 501 } 502 return TokenIDENT; 503 } 504 505 517 public int findClosingPeer(int start, final char openingPeer, final char closingPeer) { 518 return findClosingPeer(start, UNBOUND, openingPeer, closingPeer); 519 } 520 521 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 572 public int findOpeningPeer(int start, char openingPeer, char closingPeer) { 573 return findOpeningPeer(start, UNBOUND, openingPeer, closingPeer); 574 } 575 576 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 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 643 public int findNonWhitespaceForward(int position, int bound) { 644 return scanForward(position, bound, fNonWSDefaultPart); 645 } 646 647 655 public int findNonWhitespaceForwardInAnyPartition(int position, int bound) { 656 return scanForward(position, bound, fNonWS); 657 } 658 659 668 public int findNonWhitespaceBackward(int position, int bound) { 669 return scanBackward(position, bound, fNonWSDefaultPart); 670 } 671 672 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 715 public int scanForward(int position, int bound, char ch) { 716 return scanForward(position, bound, new CharacterMatch(ch)); 717 } 718 719 729 public int scanForward(int position, int bound, char[] chars) { 730 return scanForward(position, bound, new CharacterMatch(chars)); 731 } 732 733 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 774 public int scanBackward(int position, int bound, char ch) { 775 return scanBackward(position, bound, new CharacterMatch(ch)); 776 } 777 778 788 public int scanBackward(int position, int bound, char[] chars) { 789 return scanBackward(position, bound, new CharacterMatch(chars)); 790 } 791 792 798 public boolean isDefaultPartition(int position) { 799 return fPartition.equals(getPartition(position).getType()); 800 } 801 802 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"); } 819 } 820 821 return fCachedPartition; 822 } 823 824 832 private boolean contains(IRegion region, int position) { 833 int offset= region.getOffset(); 834 return offset <= position && position < offset + region.getLength(); 835 } 836 837 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 905 public boolean looksLikeClassInstanceCreationBackward(int start, int bound) { 906 int token= previousToken(start - 1, bound); 907 if (token == Symbols.TokenIDENT) { token= previousToken(getPosition(), bound); 909 while (token == Symbols.TokenOTHER) { token= previousToken(getPosition(), bound); 911 if (token != Symbols.TokenIDENT) return false; 913 token= previousToken(getPosition(), bound); 914 } 915 return token == Symbols.TokenNEW; 916 } 917 return false; 918 } 919 920 930 public static boolean isGenericStarter(CharSequence identifier) { 931 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 |