1 19 20 package org.netbeans.editor.ext; 21 22 import java.util.Map ; 23 import java.util.HashMap ; 24 import javax.swing.event.DocumentListener ; 25 import javax.swing.event.DocumentEvent ; 26 import javax.swing.text.BadLocationException ; 27 import javax.swing.text.JTextComponent ; 28 import org.netbeans.editor.BaseDocument; 29 import org.netbeans.editor.SyntaxSupport; 30 import org.netbeans.editor.Utilities; 31 import org.netbeans.editor.TokenProcessor; 32 import org.netbeans.editor.TokenID; 33 import org.netbeans.editor.TokenContextPath; 34 import org.netbeans.editor.TokenItem; 35 import org.netbeans.editor.SettingsNames; 36 import org.netbeans.editor.FinderFactory; 37 import org.netbeans.editor.TextBatchProcessor; 38 import org.netbeans.editor.Analyzer; 39 40 46 47 public class ExtSyntaxSupport extends SyntaxSupport { 48 49 51 52 public static final int COMPLETION_POPUP = 0; 53 54 public static final int COMPLETION_CANCEL = 1; 55 56 public static final int COMPLETION_REFRESH = 2; 57 58 public static final int COMPLETION_POST_REFRESH = 3; 59 60 public static final int COMPLETION_HIDE = 4; 61 62 private static final TokenID[] EMPTY_TOKEN_ID_ARRAY = new TokenID[0]; 63 64 67 private DocumentListener docL; 68 69 70 private HashMap localVarMaps = new HashMap (); 71 72 73 private HashMap globalVarMaps = new HashMap (); 74 75 public ExtSyntaxSupport(BaseDocument doc) { 76 super(doc); 77 78 docL = new DocumentListener () { 80 public void insertUpdate(DocumentEvent evt) { 81 documentModified(evt); 82 } 83 84 public void removeUpdate(DocumentEvent evt) { 85 documentModified(evt); 86 } 87 88 public void changedUpdate(DocumentEvent evt) { 89 } 90 }; 91 getDocument().addDocumentListener(docL); 92 } 93 94 112 public TokenItem getTokenChain(int startOffset, int endOffset) 113 throws BadLocationException { 114 115 if (startOffset < 0) { 116 throw new IllegalArgumentException ("startOffset=" + startOffset + " < 0"); } 118 if (startOffset > endOffset) { 119 throw new IllegalArgumentException ("startOffset=" + startOffset + " > endOffset=" + endOffset); } 122 TokenItem chain = null; 123 BaseDocument doc = getDocument(); 124 doc.readLock(); 125 try { 126 int docLen = doc.getLength(); 127 endOffset = Math.min(endOffset, docLen); 128 if( startOffset < docLen ) { 129 TokenItemTP tp = new TokenItemTP(); 130 tp.targetOffset = endOffset; 131 tokenizeText(tp, startOffset, endOffset, false); 132 chain = tp.getTokenChain(); 133 } 134 } finally { 135 doc.readUnlock(); 136 } 137 138 return chain; 139 } 140 141 145 protected void documentModified(DocumentEvent evt) { 146 localVarMaps.clear(); 148 globalVarMaps.clear(); 149 } 150 151 155 protected BracketFinder getMatchingBracketFinder(char bracketChar) { 156 BracketFinder bf = new BracketFinder(bracketChar); 157 if (bf.moveCount == 0) { bf = null; 159 } 160 161 return bf; 162 } 163 164 174 public int[] findMatchingBlock(int offset, boolean simpleSearch) 175 throws BadLocationException { 176 char bracketChar = getDocument().getChars(offset, 1)[0]; 177 int foundPos = -1; 178 179 final BracketFinder bf = getMatchingBracketFinder(bracketChar); 180 181 if (bf != null) { if (!simpleSearch) { 183 TokenID tokenID = getTokenID(offset); 184 TokenID[] bst = getBracketSkipTokens(); 185 for (int i = bst.length - 1; i >= 0; i--) { 186 if (tokenID == bst[i]) { 187 simpleSearch = true; break; 189 } 190 } 191 } 192 193 if (simpleSearch) { if (bf.isForward()) { 195 foundPos = getDocument().find(bf, offset, -1); 196 } else { 197 foundPos = getDocument().find(bf, offset + 1, 0); 198 } 199 200 } else { TextBatchProcessor tbp = new TextBatchProcessor() { 202 public int processTextBatch(BaseDocument doc, int startPos, int endPos, 203 boolean lastBatch) { 204 try { 205 int[] blks = getTokenBlocks(startPos, endPos, getBracketSkipTokens()); 206 return findOutsideBlocks(bf, startPos, endPos, blks); 207 } catch (BadLocationException e) { 208 return -1; 209 } 210 } 211 }; 212 213 if (bf.isForward()) { 214 foundPos = getDocument().processText(tbp, offset, -1); 215 } else { 216 foundPos = getDocument().processText(tbp, offset + 1, 0); 217 } 218 } 219 } 220 221 return (foundPos != -1) ? new int[] { foundPos, foundPos + 1 } : null; 222 } 223 224 228 protected TokenID[] getBracketSkipTokens() { 229 return EMPTY_TOKEN_ID_ARRAY; 230 } 231 232 237 public TokenID getTokenID(int offset) throws BadLocationException { 238 FirstTokenTP fttp = new FirstTokenTP(); 239 tokenizeText(fttp, offset, getDocument().getLength(), true); 240 return fttp.getTokenID(); 241 } 242 243 251 public int[] getFunctionBlock(int[] identifierBlock) throws BadLocationException { 252 if (identifierBlock != null) { 253 int nwPos = Utilities.getFirstNonWhiteFwd(getDocument(), identifierBlock[1]); 254 if ((nwPos >= 0) && (getDocument().getChars(nwPos, 1)[0] == '(')) { 255 return new int[] { identifierBlock[0], nwPos + 1 }; 256 } 257 } 258 return null; 259 } 260 261 public int[] getFunctionBlock(int offset) throws BadLocationException { 262 return getFunctionBlock(Utilities.getIdentifierBlock(getDocument(), offset)); 263 } 264 265 public boolean isWhitespaceToken(TokenID tokenID, 266 char[] buffer, int offset, int tokenLength) { 267 return Analyzer.isWhitespace(buffer, offset, tokenLength); 268 } 269 270 public boolean isCommentOrWhitespace(int startPos, int endPos) 271 throws BadLocationException { 272 CommentOrWhitespaceTP tp= new CommentOrWhitespaceTP(getCommentTokens()); 273 tokenizeText(tp, startPos, endPos, true); 274 return !tp.nonEmpty; 275 } 276 277 279 public int getRowLastValidChar(int offset) 280 throws BadLocationException { 281 return Utilities.getRowLastNonWhite(getDocument(), offset); 282 } 283 284 287 public boolean isRowValid(int offset) 288 throws BadLocationException { 289 return Utilities.isRowWhite(getDocument(), offset); 290 } 291 292 295 public TokenID[] getCommentTokens() { 296 return EMPTY_TOKEN_ID_ARRAY; 297 } 298 299 304 public int[] getCommentBlocks(int startPos, int endPos) 305 throws BadLocationException { 306 return getTokenBlocks(startPos, endPos, getCommentTokens()); 307 } 308 309 316 public Object findType(String varName, int varPos) { 317 Object type = null; 318 Map varMap = getLocalVariableMap(varPos); if (varMap != null) { 320 type = varMap.get(varName); 321 } 322 323 if (type == null) { 324 varMap = getGlobalVariableMap(varPos); if (varMap != null) { 326 type = varMap.get(varName); 327 } 328 } 329 330 return type; 331 } 332 333 public Map getLocalVariableMap(int offset) { 334 Integer posI = new Integer (offset); 335 Map varMap = (Map )localVarMaps.get(posI); 336 if (varMap == null) { 337 varMap = buildLocalVariableMap(offset); 338 localVarMaps.put(posI, varMap); 339 } 340 return varMap; 341 } 342 343 protected Map buildLocalVariableMap(int offset) { 344 int methodStartPos = getMethodStartPosition(offset); 345 if (methodStartPos >= 0 && methodStartPos < offset) { 346 VariableMapTokenProcessor vmtp = createVariableMapTokenProcessor(methodStartPos, offset); 347 try { 348 tokenizeText(vmtp, methodStartPos, offset, true); 349 return vmtp.getVariableMap(); 350 } catch (BadLocationException e) { 351 } 353 } 354 return null; 355 } 356 357 public Map getGlobalVariableMap(int offset) { 358 Integer posI = new Integer (offset); 359 Map varMap = (Map )globalVarMaps.get(posI); 360 if (varMap == null) { 361 varMap = buildGlobalVariableMap(offset); 362 globalVarMaps.put(posI, varMap); 363 } 364 return varMap; 365 } 366 367 protected Map buildGlobalVariableMap(int offset) { 368 int docLen = getDocument().getLength(); 369 VariableMapTokenProcessor vmtp = createVariableMapTokenProcessor(0, docLen); 370 if (vmtp != null) { 371 try { 372 tokenizeText(vmtp, 0, docLen, true); 373 return vmtp.getVariableMap(); 374 } catch (BadLocationException e) { 375 } 377 } 378 return null; 379 } 380 381 384 protected int getMethodStartPosition(int offset) { 385 return 0; } 387 388 392 public int findDeclarationPosition(String varName, int varPos) { 393 int offset = findLocalDeclarationPosition(varName, varPos); 394 if (offset < 0) { 395 offset = findGlobalDeclarationPosition(varName, varPos); 396 } 397 return offset; 398 } 399 400 public int findLocalDeclarationPosition(String varName, int varPos) { 401 int methodStartPos = getMethodStartPosition(varPos); 402 if (methodStartPos >= 0 && methodStartPos < varPos) { 403 return findDeclarationPositionImpl(varName, methodStartPos, varPos); 404 } 405 return -1; 406 } 407 408 412 public int findGlobalDeclarationPosition(String varName, int varPos) { 413 return findDeclarationPositionImpl(varName, 0, getDocument().getLength()); 414 } 415 416 private int findDeclarationPositionImpl(String varName, int startPos, int endPos) { 417 DeclarationTokenProcessor dtp = createDeclarationTokenProcessor(varName, startPos, endPos); 418 if (dtp != null) { 419 try { 420 tokenizeText(dtp, startPos, endPos, true); 421 return dtp.getDeclarationPosition(); 422 } catch (BadLocationException e) { 423 } 425 } 426 return -1; 427 } 428 429 protected DeclarationTokenProcessor createDeclarationTokenProcessor( 430 String varName, int startPos, int endPos) { 431 return null; 432 } 433 434 protected VariableMapTokenProcessor createVariableMapTokenProcessor( 435 int startPos, int endPos) { 436 return null; 437 } 438 439 440 441 public int checkCompletion(JTextComponent target, String typedText, boolean visible ) { 442 return visible ? COMPLETION_HIDE : COMPLETION_CANCEL; 443 } 444 445 448 public interface DeclarationTokenProcessor extends TokenProcessor { 449 450 451 public int getDeclarationPosition(); 452 453 } 454 455 public interface VariableMapTokenProcessor extends TokenProcessor { 456 457 458 public Map getVariableMap(); 459 460 } 461 462 465 public class BracketFinder extends FinderFactory.GenericFinder { 466 467 468 protected char bracketChar; 469 470 471 protected char matchChar; 472 473 474 private int depth; 475 476 479 protected int moveCount; 480 481 484 public BracketFinder(char bracketChar) { 485 this.bracketChar = bracketChar; 486 487 updateStatus(); 488 489 forward = (moveCount > 0); 490 } 491 492 496 protected boolean updateStatus() { 497 boolean valid = true; 498 switch (bracketChar) { 499 case '(': 500 matchChar = ')'; 501 moveCount = +1; 502 break; 503 case ')': 504 matchChar = '('; 505 moveCount = -1; 506 break; 507 case '{': 508 matchChar = '}'; 509 moveCount = +1; 510 break; 511 case '}': 512 matchChar = '{'; 513 moveCount = -1; 514 break; 515 case '[': 516 matchChar = ']'; 517 moveCount = +1; 518 break; 519 case ']': 520 matchChar = '['; 521 moveCount = -1; 522 break; 523 case '<': 524 matchChar = '>'; 525 moveCount = +1; 526 break; 527 case '>': 528 matchChar = '<'; 529 moveCount = -1; 530 break; 531 default: 532 valid = false; 533 } 534 return valid; 535 } 536 537 protected int scan(char ch, boolean lastChar) { 538 if (ch == bracketChar) { 539 depth++; 540 } else if (ch == matchChar) { 541 if (--depth == 0) { 542 found = true; 543 return 0; 544 } 545 } 546 return moveCount; 547 } 548 549 } 550 551 552 final class TokenItemTP implements TokenProcessor { 553 554 private Item firstItem; 555 556 private Item lastItem; 557 558 private int fwdBatchLineCnt; 559 private int bwdBatchLineCnt; 560 561 private char[] buffer; 562 563 private int bufferStartPos; 564 565 570 int targetOffset; 571 572 573 TokenItemTP() { 574 fwdBatchLineCnt = bwdBatchLineCnt = ((Integer )getDocument().getProperty( 575 SettingsNames.LINE_BATCH_SIZE)).intValue(); 576 } 577 578 public TokenItem getTokenChain() { 579 return firstItem; 580 } 581 582 public boolean token(TokenID tokenID, TokenContextPath tokenContextPath, 583 int tokenBufferOffset, int tokenLength) { 584 if (bufferStartPos + tokenBufferOffset >= targetOffset) { return false; 586 } 587 588 lastItem = new Item(tokenID, tokenContextPath, 589 bufferStartPos + tokenBufferOffset, 590 new String (buffer, tokenBufferOffset, tokenLength), lastItem 591 ); 592 593 if (firstItem == null) { firstItem = lastItem; 595 } 596 597 return true; 598 } 599 600 public int eot(int offset) { 601 return ((Integer )getDocument().getProperty(SettingsNames.MARK_DISTANCE)).intValue(); 602 } 603 604 public void nextBuffer(char[] buffer, int offset, int len, 605 int startPos, int preScan, boolean lastBuffer) { 606 this.buffer = buffer; 607 bufferStartPos = startPos - offset; 608 } 609 610 Item getNextChunk(Item i) { 611 BaseDocument doc = getDocument(); 612 int itemEndPos = i.getOffset() + i.getImage().length(); 613 int docLen = doc.getLength(); 614 if (itemEndPos == docLen) { 615 return null; 616 } 617 618 int endPos; 619 try { 620 endPos = Utilities.getRowStart(doc, itemEndPos, fwdBatchLineCnt); 621 } catch (BadLocationException e) { 622 return null; 623 } 624 625 if (endPos == -1) { endPos = docLen; 627 } 628 fwdBatchLineCnt *= 2; 630 631 Item nextChunkHead = null; 632 Item fit = firstItem; 633 Item lit = lastItem; 634 try { 635 firstItem = null; 637 lastItem = null; 638 targetOffset = endPos; 639 640 tokenizeText(this, itemEndPos, endPos, false); 641 nextChunkHead = firstItem; 642 643 } catch (BadLocationException e) { 644 } finally { 645 if (firstItem != null) { 647 lit.next = firstItem; 648 firstItem.previous = lit; 649 } 650 651 firstItem = fit; 652 if (lastItem == null) { lastItem = lit; 654 } 655 } 656 657 return nextChunkHead; 658 } 659 660 Item getPreviousChunk(Item i) { 661 BaseDocument doc = getDocument(); 662 int itemStartPos = i.getOffset(); 663 if (itemStartPos == 0) { 664 return null; 665 } 666 667 int startPos; 668 try { 669 startPos = Utilities.getRowStart(doc, itemStartPos, -bwdBatchLineCnt); 670 } catch (BadLocationException e) { 671 return null; 672 } 673 674 if (startPos == -1) { startPos = 0; 676 } 677 bwdBatchLineCnt *= 2; 678 679 Item previousChunkLast = null; 680 Item fit = firstItem; 681 Item lit = lastItem; 682 try { 683 firstItem = null; 685 lastItem = null; 686 targetOffset = itemStartPos; 687 688 tokenizeText(this, startPos, itemStartPos, false); 689 previousChunkLast = lastItem; 690 691 } catch (BadLocationException e) { 692 } finally { 693 if (lastItem != null) { 695 fit.previous = lastItem; 696 lastItem.next = fit; 697 } 698 699 lastItem = lit; 700 if (firstItem == null) { firstItem = fit; 702 } 703 } 704 705 return previousChunkLast; 706 } 707 708 final class Item extends TokenItem.AbstractItem { 709 710 Item previous; 711 712 TokenItem next; 713 714 Item(TokenID tokenID, TokenContextPath tokenContextPath, 715 int offset, String image, Item previous) { 716 super(tokenID, tokenContextPath, offset, image); 717 if (previous != null) { 718 this.previous = previous; 719 previous.next = this; 720 } 721 } 722 723 public TokenItem getNext() { 724 if (next == null) { 725 next = getNextChunk(this); 726 } 727 return next; 728 } 729 730 public TokenItem getPrevious() { 731 if (previous == null) { 732 previous = getPreviousChunk(this); 733 } 734 return previous; 735 } 736 737 } 738 739 } 740 741 742 class CommentOrWhitespaceTP implements TokenProcessor { 743 744 private char[] buffer; 745 746 private TokenID[] commentTokens; 747 748 boolean nonEmpty; 749 750 CommentOrWhitespaceTP(TokenID[] commentTokens) { 751 this.commentTokens = commentTokens; 752 } 753 754 public boolean token(TokenID tokenID, TokenContextPath tokenContextPath, 755 int offset, int tokenLength) { 756 for (int i = 0; i < commentTokens.length; i++) { 757 if (tokenID == commentTokens[i]) { 758 return true; } 760 } 761 boolean nonWS = isWhitespaceToken(tokenID, buffer, offset, tokenLength); 762 if (nonWS) { 763 nonEmpty = true; 764 } 765 return nonWS; 766 } 767 768 public int eot(int offset) { 769 return 0; 770 } 771 772 public void nextBuffer(char[] buffer, int offset, int len, 773 int startPos, int preScan, boolean lastBuffer) { 774 this.buffer = buffer; 775 } 776 777 } 778 779 static class FirstTokenTP implements TokenProcessor { 780 781 private TokenID tokenID; 782 783 public TokenID getTokenID() { 784 return tokenID; 785 } 786 787 public boolean token(TokenID tokenID, TokenContextPath tokenContextPath, 788 int offset, int tokenLen) { 789 this.tokenID = tokenID; 790 return false; } 792 793 public int eot(int offset) { 794 return 0; 795 } 796 797 public void nextBuffer(char[] buffer, int offset, int len, 798 int startPos, int preScan, boolean lastBuffer) { 799 } 800 801 } 802 803 } 804 | Popular Tags |