1 33 34 package edu.rice.cs.drjava.model.definitions; 35 36 import javax.swing.text.*; 37 import javax.swing.undo.*; 38 import javax.swing.event.DocumentEvent ; 39 import java.util.LinkedList ; 40 import java.util.List ; 41 import java.util.Map ; 42 import java.util.WeakHashMap ; 43 import java.lang.ref.WeakReference ; 44 45 import java.io.File ; 46 47 import edu.rice.cs.drjava.model.definitions.reducedmodel.*; 48 import edu.rice.cs.util.Log; 49 import edu.rice.cs.util.UnexpectedException; 50 import edu.rice.cs.util.swing.Utilities; 51 import edu.rice.cs.drjava.model.definitions.indent.Indenter; 52 import edu.rice.cs.drjava.model.OpenDefinitionsDocument; 53 import edu.rice.cs.drjava.model.*; 54 55 56 72 public class DefinitionsDocument extends AbstractDJDocument implements Finalizable<DefinitionsDocument> { 73 74 public static final Log _log = new Log("GlobalModel.txt", false); 75 private static final int NO_COMMENT_OFFSET = 0; 76 private static final int WING_COMMENT_OFFSET = 2; 77 78 private volatile List <DocumentClosedListener> _closedListeners = new LinkedList <DocumentClosedListener>(); 79 80 public void addDocumentClosedListener(DocumentClosedListener l) { 81 synchronized(_closedListeners) { _closedListeners.add(l); } 82 } 83 84 public void removeDocumentClosedListener(DocumentClosedListener l) { 85 synchronized(_closedListeners) { _closedListeners.remove(l); } 86 } 87 88 90 96 98 public void close() { 99 _removeIndenter(); 100 synchronized(_closedListeners) { 101 for (DocumentClosedListener l: _closedListeners) { l.close(); } 102 _closedListeners = new LinkedList <DocumentClosedListener>(); 103 } 104 } 105 106 108 109 private static final int UNDO_LIMIT = 1000; 110 111 private static boolean _tabsRemoved = true; 112 113 private volatile boolean _isModifiedSinceSave = false; 114 115 private volatile boolean _classFileInSync = false; 116 117 private volatile int _cachedLocation; 118 119 private volatile int _cachedLineNum; 120 121 private volatile int _cachedPrevLineLoc; 122 123 private volatile int _cachedNextLineLoc; 124 125 126 private volatile String _packageName; 127 128 private volatile File _classFile; 129 130 132 private volatile OpenDefinitionsDocument _odd; 133 134 private volatile CompoundUndoManager _undoManager; 135 136 137 private final GlobalEventNotifier _notifier; 138 139 142 143 144 private volatile LinkedList <WeakReference <WrappedPosition>> _wrappedPosList; 145 146 150 public DefinitionsDocument(Indenter indenter, GlobalEventNotifier notifier) { 151 super(indenter); 152 _notifier = notifier; 153 _init(); 154 resetUndoManager(); 155 } 156 157 161 public DefinitionsDocument(GlobalEventNotifier notifier) { 162 super(); 163 _notifier = notifier; 164 _init(); 165 resetUndoManager(); 166 } 167 168 172 public DefinitionsDocument(GlobalEventNotifier notifier, CompoundUndoManager undoManager) { 173 super(); 174 _notifier = notifier; 175 _init(); 176 _undoManager = undoManager; 177 } 178 179 180 185 186 protected Indenter makeNewIndenter(int indentLevel) { return new Indenter(indentLevel); } 187 188 189 private void _init() { 190 _odd = null; 191 _cachedLocation = 0; 192 _cachedLineNum = 1; 193 _cachedPrevLineLoc = -1; 194 _cachedNextLineLoc = -1; 195 _classFile = null; 196 _cacheInUse = false; 197 } 198 199 200 201 204 public void setOpenDefDoc(OpenDefinitionsDocument odd) { if (_odd == null) _odd = odd; } 205 206 207 public OpenDefinitionsDocument getOpenDefDoc() { 208 if (_odd == null) 209 throw new IllegalStateException ("The OpenDefinitionsDocument for this DefinitionsDocument has never been set"); 210 else return _odd; 211 } 212 213 protected void _styleChanged() { 214 acquireWriteLock(); 215 try { 216 int length = getLength() - _currentLocation; 217 218 DocumentEvent evt = new DefaultDocumentEvent(_currentLocation, length, DocumentEvent.EventType.CHANGE); 220 fireChangedUpdate(evt); 221 } 222 finally { releaseWriteLock(); } 223 } 224 225 226 231 235 242 278 279 292 293 296 public String getQualifiedClassName() throws ClassNameNotFoundException { 297 return _getPackageQualifier() + getMainClassName(); 298 } 299 300 301 public String getQualifiedClassName(int pos) throws ClassNameNotFoundException { 302 return _getPackageQualifier() + getEnclosingTopLevelClassName(pos); 303 } 304 305 308 protected String _getPackageQualifier() { 309 String packageName = getPackageName(); 310 if ((packageName != null) && (! packageName.equals(""))) { packageName = packageName + "."; } 311 return packageName; 312 } 313 314 public void setClassFileInSync(boolean inSync) { _classFileInSync = inSync; } 315 316 public boolean getClassFileInSync() { return _classFileInSync; } 317 318 public void setCachedClassFile(File classFile) { _classFile = classFile; } 319 320 public File getCachedClassFile() { return _classFile; } 321 322 325 public void insertString(int offset, String str, AttributeSet a) throws BadLocationException { 326 327 332 acquireWriteLock(); 333 try { 334 if (_tabsRemoved) str = _removeTabs(str); 335 setModifiedSinceSave(); 336 super.insertString(offset, str, a); 337 } 338 finally { releaseWriteLock(); } 339 } 340 341 342 345 public void remove(int offset, int len) throws BadLocationException { 346 347 if (len == 0) return; 348 349 acquireWriteLock(); 350 try { 351 setModifiedSinceSave(); 352 super.remove(offset, len); 353 } 354 finally { releaseWriteLock(); } 355 } 356 357 362 static String _removeTabs(final String source) { 363 return source.replace('\t', ' '); 365 } 366 367 369 public void updateModifiedSinceSave() { 370 371 acquireWriteLock(); 372 try { 373 _isModifiedSinceSave = _undoManager.isModified(); 374 } 376 finally { 377 if (! _isModifiedSinceSave && _odd != null) _odd.documentReset(); 378 releaseWriteLock(); 379 } 381 } 382 383 385 private void setModifiedSinceSave() { 386 if (! _isModifiedSinceSave) { 387 _isModifiedSinceSave = true; 388 _classFileInSync = false; 389 if (_odd != null) _odd.documentModified(); 390 } 391 } 392 393 394 public void resetModification() { 395 acquireWriteLock(); 396 try { 397 _isModifiedSinceSave = false; 398 _undoManager.documentSaved(); 399 } 400 finally { 401 if (_odd != null) _odd.documentReset(); releaseWriteLock(); 403 404 } 405 } 406 407 410 public boolean isModifiedSinceSave() { 411 acquireReadLock(); 412 try { return _isModifiedSinceSave; } 413 finally { releaseReadLock(); } 414 } 415 416 417 public int getCurrentCol() { 418 int here = _currentLocation; 420 int startOfLine = getLineStartPos(here); 421 return here - startOfLine; 422 } 423 424 425 public int getCurrentLine() { 426 acquireReadLock(); 427 try { 428 synchronized(_reduced) { 429 int here = _currentLocation; 430 if (_cachedLocation > getLength()) { 431 _cachedLocation = 0; 433 _cachedLineNum = 1; 434 } 435 if (_cachedNextLineLoc > getLength()) _cachedNextLineLoc = -1; 436 if ( ! (_cachedPrevLineLoc < here && here < _cachedNextLineLoc )) { 438 439 if (_cachedLocation - here > here) { 442 _cachedLocation = 0; 443 _cachedLineNum = 1; 444 } 445 int lineOffset = _getRelativeLine(); 446 _cachedLineNum = _cachedLineNum + lineOffset; 447 448 } 449 _cachedLocation = here; 450 _cachedPrevLineLoc = getLineStartPos(here); 451 _cachedNextLineLoc = getLineEndPos(here); 452 return _cachedLineNum; 453 } 454 } 455 finally { releaseReadLock(); } 456 } 457 458 461 private int _getRelativeLine() { 462 463 int count = 0; 464 int currLoc = _currentLocation; 465 setCurrentLocation(_cachedLocation); 466 467 if (_cachedLocation > currLoc) { 468 int prevLineLoc = getLineStartPos( _cachedLocation ); 470 while (prevLineLoc > currLoc) { 471 count--; 472 prevLineLoc = getLineStartPos( prevLineLoc - 1 ); 473 } 474 } 475 476 else { 477 int nextLineLoc = getLineEndPos( _cachedLocation ); 479 while (nextLineLoc < currLoc) { 480 count++; 481 nextLineLoc = getLineEndPos( nextLineLoc + 1 ); 482 } 483 } 484 setCurrentLocation(currLoc); 485 return count; 486 } 487 488 493 public int getOffset(int lineNum) { 494 if (lineNum < 0) return -1; 495 String defsText = getText(); 496 int curLine = 1; 497 int offset = 0; 499 while (offset < defsText.length()) { 502 503 if (curLine == lineNum) return offset; 504 505 int nextNewline = defsText.indexOf('\n', offset); 506 if (nextNewline == -1) return -1; 508 curLine++; 509 offset = nextNewline + 1; 510 } 511 return -1; 512 } 513 514 515 public boolean tabsRemoved() { return _tabsRemoved; } 516 517 522 public int commentLines(int selStart, int selEnd) { 523 524 int toReturn = selEnd; 526 if (selStart == selEnd) { 527 acquireWriteLock(); 528 try { 529 synchronized(_reduced) { 530 setCurrentLocation(selStart); 531 Position oldCurrentPosition = createUnwrappedPosition(_currentLocation); 532 _commentLine(); 533 toReturn += WING_COMMENT_OFFSET; 534 } 537 } 538 catch (BadLocationException e) { throw new UnexpectedException(e); } 539 finally { releaseWriteLock(); } 540 } 541 else toReturn = _commentBlock(selStart, selEnd); 542 _undoManager.endLastCompoundEdit(); return toReturn; 544 } 545 546 547 553 private int _commentBlock(final int start, final int end) { 554 int afterCommentEnd = end; 555 acquireWriteLock(); 556 try { 557 final Position endPos = this.createUnwrappedPosition(end); 560 int walker = start; 562 synchronized(_reduced) { 563 while (walker < endPos.getOffset()) { 564 setCurrentLocation(walker); 565 Position walkerPos = this.createUnwrappedPosition(walker); 568 _commentLine(); afterCommentEnd += WING_COMMENT_OFFSET; 571 setCurrentLocation(walkerPos.getOffset()); 573 walker = walkerPos.getOffset(); 574 walker += _reduced.getDistToNextNewline() + 1; 578 } 580 } 581 } 582 catch (BadLocationException e) { throw new UnexpectedException(e); } 583 finally { releaseWriteLock(); } 584 return afterCommentEnd; 585 } 586 587 589 private void _commentLine() { 590 try { insertString(_currentLocation - getCurrentCol(), "//", null); } 593 catch (BadLocationException e) { throw new UnexpectedException(e); } 594 } 595 596 601 public int uncommentLines(int selStart, int selEnd) { 602 603 int toReturn = selEnd; 605 if (selStart == selEnd) { 606 acquireWriteLock(); 607 try { 608 synchronized(_reduced) { 609 setCurrentLocation(selStart); 610 Position oldCurrentPosition = createUnwrappedPosition(_currentLocation); 611 _uncommentLine(); toReturn -= WING_COMMENT_OFFSET; 613 } 617 } 618 catch (BadLocationException e) { throw new UnexpectedException(e); } 619 finally { releaseWriteLock(); } 620 } 621 else toReturn = _uncommentBlock(selStart, selEnd); 622 _undoManager.endLastCompoundEdit(); 624 return toReturn; 625 } 626 627 632 private int _uncommentBlock(final int start, final int end) { 633 int afterUncommentEnd = end; 634 acquireWriteLock(); 635 try { 636 final Position endPos = this.createUnwrappedPosition(end); 639 int walker = start; 641 synchronized(_reduced) { 642 while (walker < endPos.getOffset()) { 643 setCurrentLocation(walker); 644 Position walkerPos = this.createUnwrappedPosition(walker); 647 afterUncommentEnd-= _uncommentLine(); setCurrentLocation(walkerPos.getOffset()); 651 walker = walkerPos.getOffset(); 652 walker += _reduced.getDistToNextNewline() + 1; 656 } 658 } 659 } 660 catch (BadLocationException e) { throw new UnexpectedException(e); } 661 finally { releaseWriteLock(); } 662 return afterUncommentEnd; 663 } 664 665 669 private int _uncommentLine() throws BadLocationException { 670 int curCol = getCurrentCol(); 672 int lineStart = _currentLocation - curCol; 673 String text = getText(lineStart, curCol + _reduced.getDistToNextNewline()); 674 int pos = text.indexOf("//"); 675 676 679 boolean goodWing = true; 681 for (int i = pos-1; i >= 0; i--) { 682 char c = text.charAt(i); 683 if (c != ' ') { 685 goodWing = false; 686 return NO_COMMENT_OFFSET; 687 } 688 } 689 690 if (pos >= 0 && goodWing) { 693 remove(lineStart + pos, 2); 695 return WING_COMMENT_OFFSET; 697 } 698 return NO_COMMENT_OFFSET; 699 } 700 701 702 public void gotoLine(int line) { 703 704 int dist; 705 if (line < 0) return; 706 int actualLine =1; 707 708 acquireReadLock(); 709 int len = getLength(); 710 try { 711 synchronized(_reduced) { 712 setCurrentLocation(0); 713 for (int i = 1; (i < line) && (_currentLocation < len); i++) { 714 dist = _reduced.getDistToNextNewline(); 715 if (_currentLocation + dist < len) dist++; 716 actualLine++; 717 move(dist); 718 } 719 _cachedLineNum = actualLine; 720 _cachedLocation = _currentLocation; 721 _cachedPrevLineLoc = getLineStartPos(_currentLocation); 722 _cachedNextLineLoc = getLineEndPos(_currentLocation); 723 } 724 } 725 finally { releaseReadLock(); } 726 } 727 728 private int _findNextOpenSquiggly(String text, int pos) throws BadLocationException { 729 int i; 731 int reducedPos = pos; 732 733 synchronized(_reduced) { 734 final int origLocation = _currentLocation; 735 _reduced.move(pos - origLocation); 738 i = text.indexOf('{', reducedPos); 740 while(i>-1) { 741 _reduced.move(i - reducedPos); reducedPos = i; 745 ReducedModelState state = _reduced.getStateAtCurrent(); 747 if (!state.equals(ReducedModelState.FREE) || _isStartOfComment(text, i) 748 || ((i > 0) && _isStartOfComment(text, i - 1))) { 749 i = text.indexOf('{', reducedPos+1); 750 continue; } 752 else { 753 break; } 755 } 757 _reduced.move(origLocation - reducedPos); } 759 760 if (i == -1) reducedPos = ERROR_INDEX; return reducedPos; 762 } 763 764 private int _findPrevKeyword(String text, String kw, int pos) throws BadLocationException { 765 int i; 767 int reducedPos = pos; 768 769 synchronized(_reduced) { 770 final int origLocation = _currentLocation; 771 _reduced.move(pos - origLocation); 774 i = text.lastIndexOf(kw, reducedPos); 776 while(i >- 1) { 777 if (i > 0) { 779 if (Character.isJavaIdentifierPart(text.charAt(i-1))) { 780 i = text.lastIndexOf(kw, i-1); 782 continue; } 784 } 785 if (i+kw.length()<text.length()) { 787 if (Character.isJavaIdentifierPart(text.charAt(i+kw.length()))) { 788 i = text.lastIndexOf(kw, i-1); 790 continue; } 792 } 793 794 _reduced.move(i - reducedPos); reducedPos = i; 798 ReducedModelState state = _reduced.getStateAtCurrent(); 800 if (!state.equals(ReducedModelState.FREE) || _isStartOfComment(text, i) 801 || ((i > 0) && _isStartOfComment(text, i - 1))) { 802 i = text.lastIndexOf(kw, reducedPos-1); 803 continue; } 805 else { 806 break; } 808 } 810 _reduced.move(origLocation - reducedPos); } 812 813 if (i == -1) reducedPos = ERROR_INDEX; return reducedPos; 815 } 816 817 819 825 public String getEnclosingClassName(int pos, boolean qual) throws BadLocationException, ClassNameNotFoundException { 826 final StringBuilder keyBuf = new StringBuilder ("getEnclosingClassName:").append(pos); 829 keyBuf.append(":").append(qual); 830 String key = keyBuf.toString(); 831 String cached = (String ) _checkCache(key); 832 if (cached != null) return cached; 833 834 char[] delims = {'{','}','(',')','[',']','+','-','/','*',';',':','=', 835 '!','@','#','$','%','^','~','\\','"','`','|'}; 836 String name = ""; 837 838 acquireReadLock(); 839 try { 840 String text = getText(DOCSTART, pos+1); 841 842 int curPos = pos; 843 844 do { 845 if ((text.charAt(curPos)!='{') || (text.charAt(curPos)!='}')) { ++curPos; } 846 847 850 curPos = findPrevEnclosingBrace(curPos, '{', '}'); 851 if (curPos==ERROR_INDEX) { break; } 852 int classPos = _findPrevKeyword(text, "class", curPos); 853 int interPos = _findPrevKeyword(text, "interface", curPos); 854 int otherPos = findPrevDelimiter(curPos, delims); 855 int newPos = ERROR_INDEX; 856 int closeParenPos = findPrevNonWSCharPos(curPos); 858 if ((closeParenPos!=ERROR_INDEX) && (text.charAt(closeParenPos)==')')) { 859 int openParenPos = findPrevEnclosingBrace(closeParenPos, '(', ')'); 861 if ((openParenPos!=ERROR_INDEX) && (text.charAt(openParenPos)=='(')) { 862 newPos = _findPrevKeyword(text, "new", openParenPos); 864 if (! _isAnonymousInnerClass(newPos, curPos)) { 866 newPos = ERROR_INDEX; 868 } 869 } 870 } 871 while((classPos!=ERROR_INDEX) || (interPos!=ERROR_INDEX) || (newPos!=ERROR_INDEX)) { 875 if (newPos!=ERROR_INDEX) { 876 classPos = ERROR_INDEX; 878 interPos = ERROR_INDEX; 879 break; 880 } 881 else if ((otherPos>classPos) && (otherPos>interPos)) { 882 if ((text.charAt(otherPos)!='{') || (text.charAt(otherPos)!='}')) { ++otherPos; } 883 curPos = findPrevEnclosingBrace(otherPos, '{', '}'); 884 classPos = _findPrevKeyword(text, "class", curPos); 885 interPos = _findPrevKeyword(text, "interface", curPos); 886 otherPos = findPrevDelimiter(curPos, delims); 887 newPos = ERROR_INDEX; 888 closeParenPos = findPrevNonWSCharPos(curPos); 890 if ((closeParenPos!=ERROR_INDEX) && (text.charAt(closeParenPos)==')')) { 893 int openParenPos = findPrevEnclosingBrace(closeParenPos, '(', ')'); 895 if ((openParenPos!=ERROR_INDEX) && (text.charAt(openParenPos)=='(')) { 896 newPos = _findPrevKeyword(text, "new", openParenPos); 898 if (_isAnonymousInnerClass(newPos, curPos)) { 900 } 902 else { 903 newPos = ERROR_INDEX; 904 } 905 } 906 } 907 } 911 else { 912 curPos = Math.max(classPos, Math.max(interPos, newPos)); 914 break; 915 } 916 } 917 918 if ((classPos!=ERROR_INDEX) || (interPos!=ERROR_INDEX)) { 919 if (classPos>interPos) { 920 curPos += "class".length(); 922 } 923 else { 924 curPos += "interface".length(); 926 } 927 int nameStart = getFirstNonWSCharPos(curPos); 928 if (nameStart==ERROR_INDEX) { throw new ClassNameNotFoundException("Cannot determine enclosing class name"); } 929 int nameEnd = nameStart+1; 930 while(nameEnd<text.length()) { 931 if ((!Character.isJavaIdentifierPart(text.charAt(nameEnd))) && (text.charAt(nameEnd)!='.')) { 932 break; 934 } 935 ++nameEnd; 936 } 937 name = text.substring(nameStart,nameEnd) + '$' + name; 938 } 939 else if (newPos!=ERROR_INDEX) { 940 name = String.valueOf(_getAnonymousInnerClassIndex(curPos)) + "$" + name; 941 curPos = newPos; 942 } 943 else { 944 break; 946 } 947 } while(qual); 948 } 949 finally { releaseReadLock(); } 950 951 if (name.length()>0) name = name.substring(0, name.length()-1); 953 954 if (qual) { 955 String pn = getPackageName(); 956 if ((pn.length()>0) && (name.length()>0)) { 957 name = getPackageName() + "." + name; 958 } 959 } 960 return name; 962 } 963 964 969 private boolean _isAnonymousInnerClass(int newPos, int openSquigglyPos) throws BadLocationException { 970 975 final StringBuilder keyBuf = 977 new StringBuilder ("_getAnonymousInnerClassIndex:").append(newPos).append(':').append(openSquigglyPos); 978 String key = keyBuf.toString(); 979 Boolean cached = (Boolean ) _checkCache(key); 980 if (cached != null) { 981 return cached; 983 } 984 985 cached = false; 987 String text = getText(DOCSTART, openSquigglyPos+1); 988 int origNewPos = newPos; 989 newPos += "new".length(); 990 int classStart = getFirstNonWSCharPos(newPos); 991 if (classStart!=ERROR_INDEX) { 992 int classEnd = classStart+1; 993 while(classEnd<text.length()) { 994 if ((!Character.isJavaIdentifierPart(text.charAt(classEnd))) && (text.charAt(classEnd)!='.')) { 995 break; 997 } 998 ++classEnd; 999 } 1000 int parenStart = getFirstNonWSCharPos(classEnd); 1002 if (parenStart!=ERROR_INDEX) { 1003 int origParenStart = parenStart; 1004 1005 if (text.charAt(origParenStart)=='<') { 1007 parenStart = ERROR_INDEX; 1008 int closePointyBracket = findNextEnclosingBrace(origParenStart, '<', '>'); 1010 if (closePointyBracket!=ERROR_INDEX) { 1011 if (text.charAt(closePointyBracket)=='>') { 1012 parenStart = getFirstNonWSCharPos(closePointyBracket+1); 1013 } 1014 } 1015 } 1016 } 1017 if (parenStart!=ERROR_INDEX) { 1018 if (text.charAt(parenStart)=='(') { 1019 synchronized(_reduced) { 1020 final int origLocation = _currentLocation; 1021 _reduced.move(parenStart+1 - origLocation); int parenEnd = balanceForward(); 1023 _reduced.move(origLocation - (parenStart+1)); if (parenEnd > -1) { 1025 parenEnd = parenEnd + parenStart+1; 1026 int afterParen = getFirstNonWSCharPos(parenEnd); 1028 cached = (afterParen==openSquigglyPos); 1030 } 1031 } 1032 } 1033 } 1034 } 1035 1036 _storeInCache(key, cached); 1037 1038 return cached; 1040 } 1041 1042 1047 public String getPackageName() { 1048 try { return getStrictPackageName(); } 1049 catch(InvalidPackageException e) { return ""; } 1050 } 1051 1052 1057 int _getAnonymousInnerClassIndex(int pos) throws BadLocationException, ClassNameNotFoundException { 1058 1060 final StringBuilder keyBuf = new StringBuilder ("_getAnonymousInnerClassIndex:").append(pos); 1062 final String key = keyBuf.toString(); 1063 final Integer cached = (Integer ) _checkCache(key); 1064 if (cached != null) { 1065 return cached.intValue(); 1067 } 1068 1069 --pos; char[] delims = {'{','}','(',')','[',']','+','-','/','*',';',':','=', 1072 '!','@','#','$','%','^','~','\\','"','`','|'}; 1073 String className = getEnclosingClassName(pos, true); 1074 String text = getText(DOCSTART, pos); 1075 int index = 1; 1076 int newPos = pos; 1077 while((newPos = _findPrevKeyword(text, "new", newPos-1)) != ERROR_INDEX) { 1079 int afterNewPos = newPos + "new".length(); 1081 int classStart = getFirstNonWSCharPos(afterNewPos); 1082 if (classStart==ERROR_INDEX) { continue; } 1083 int classEnd = classStart+1; 1084 while(classEnd<text.length()) { 1085 if ((!Character.isJavaIdentifierPart(text.charAt(classEnd))) && (text.charAt(classEnd)!='.')) { 1086 break; 1088 } 1089 ++classEnd; 1090 } 1091 int parenStart = getFirstNonWSCharPos(classEnd); 1093 if (parenStart==ERROR_INDEX) { continue; } 1094 int origParenStart = parenStart; 1095 1096 if (text.charAt(origParenStart)=='<') { 1098 parenStart = ERROR_INDEX; 1099 int closePointyBracket = findNextEnclosingBrace(origParenStart, '<', '>'); 1101 if (closePointyBracket!=ERROR_INDEX) { 1102 if (text.charAt(closePointyBracket)=='>') { 1103 parenStart = getFirstNonWSCharPos(closePointyBracket+1); 1104 } 1105 } 1106 } 1107 if (parenStart==ERROR_INDEX) { continue; } 1108 if (text.charAt(parenStart)!='(') { continue; } 1109 int parenEnd = findNextEnclosingBrace(parenStart, '(', ')'); 1110 1111 int nextOpenSquiggly = _findNextOpenSquiggly(text, parenEnd); 1112 if (nextOpenSquiggly==ERROR_INDEX) { continue; } 1113 if (_isAnonymousInnerClass(newPos, nextOpenSquiggly)) { 1116 String cn = getEnclosingClassName(newPos, true); 1118 if (!cn.startsWith(className)) { break; } 1120 else if (!cn.equals(className)) { 1121 newPos = findPrevEnclosingBrace(newPos, '{', '}'); 1122 continue; 1123 } 1124 else { 1125 ++index; 1126 } 1127 } 1128 } 1129 _storeInCache(key, new Integer (index)); 1130 return index; 1132 } 1133 1134 1141 protected String getStrictPackageName() throws InvalidPackageException { 1142 1143 1145 final StringBuilder buf = new StringBuilder (); 1146 int oldLocation = 0; 1148 acquireReadLock(); 1149 try { 1150 final String text = getText(); 1151 final int docLength = text.length(); 1152 if (docLength == 0) return ""; 1153 1154 synchronized(_reduced) { 1156 oldLocation = _currentLocation; 1157 try { 1158 setCurrentLocation(0); 1159 1160 1161 int firstNormalLocation = 0; 1162 while (firstNormalLocation < docLength) { 1163 setCurrentLocation(firstNormalLocation); 1164 1165 if (_reduced.currentToken().getHighlightState() == HighlightStatus.NORMAL) { 1166 char curChar = text.charAt(firstNormalLocation); 1168 if (! Character.isWhitespace(curChar)) break; 1169 } 1170 firstNormalLocation++; 1171 } 1172 1173 1176 if (firstNormalLocation == docLength) return ""; 1177 1178 final int strlen = "package".length(); 1179 1180 final int endLocation = firstNormalLocation + strlen; 1181 1182 if ((firstNormalLocation + strlen > docLength) || 1183 ! text.substring(firstNormalLocation, endLocation).equals("package")) { 1184 return ""; 1187 } 1188 1189 int afterPackage = firstNormalLocation + strlen; 1192 1193 int semicolonLocation = afterPackage; 1194 do { 1195 semicolonLocation = text.indexOf(";", semicolonLocation + 1); 1196 if (semicolonLocation == -1) 1197 throw new InvalidPackageException(firstNormalLocation, 1198 "No semicolon found to terminate package statement!"); 1199 setCurrentLocation(semicolonLocation); 1200 } 1201 while (_reduced.currentToken().getHighlightState() != HighlightStatus.NORMAL); 1202 1203 for (int walk = afterPackage + 1; walk < semicolonLocation; walk++) { 1205 setCurrentLocation(walk); 1206 if (_reduced.currentToken().getHighlightState() == HighlightStatus.NORMAL) { 1207 char curChar = text.charAt(walk); 1208 if (! Character.isWhitespace(curChar)) buf.append(curChar); 1209 } 1210 } 1211 1212 String toReturn = buf.toString(); 1213 if (toReturn.equals("")) 1214 throw new InvalidPackageException(firstNormalLocation, 1215 "Package name was not specified after the package keyword!"); 1216 return toReturn; 1217 } 1218 finally { setCurrentLocation(0); setCurrentLocation(oldLocation); 1221 } 1222 } 1223 } 1224 finally { releaseReadLock(); } 1225 } 1226 1227 1231 public String getEnclosingTopLevelClassName(int pos) throws ClassNameNotFoundException { 1232 acquireReadLock(); 1233 synchronized(_reduced) { 1234 int oldLocation = _currentLocation; 1235 try { 1236 setCurrentLocation(pos); 1237 IndentInfo info = getIndentInformation(); 1238 1239 int topLevelBracePos = -1; 1241 String braceType = info.braceTypeCurrent; 1242 while (!braceType.equals(IndentInfo.noBrace)) { 1243 if (braceType.equals(IndentInfo.openSquiggly)) { 1244 topLevelBracePos = _currentLocation - info.distToBraceCurrent; 1245 } 1246 move(-info.distToBraceCurrent); 1247 info = getIndentInformation(); 1248 braceType = info.braceTypeCurrent; 1249 } 1250 if (topLevelBracePos == -1) { 1251 setCurrentLocation(oldLocation); 1253 throw new ClassNameNotFoundException("no top level brace found"); 1254 } 1255 1256 char[] delims = {'{', '}', ';'}; 1257 int prevDelimPos = findPrevDelimiter(topLevelBracePos, delims); 1258 if (prevDelimPos == ERROR_INDEX) { 1259 prevDelimPos = DOCSTART; 1261 } 1262 else prevDelimPos++; 1263 setCurrentLocation(oldLocation); 1264 1265 return getNextTopLevelClassName(prevDelimPos, topLevelBracePos); 1267 } 1268 catch (BadLocationException ble) { throw new UnexpectedException(ble); } 1269 finally { 1270 setCurrentLocation(oldLocation); 1271 releaseReadLock(); 1272 } 1273 } 1274 } 1275 1276 1280 private String getFirstClassName(int indexOfClass, int indexOfInterface) throws ClassNameNotFoundException { 1281 1282 if ((indexOfClass == -1) && (indexOfInterface == -1)) throw ClassNameNotFoundException.DEFAULT; 1283 if ((indexOfInterface == -1) || (indexOfClass != -1 && indexOfClass < indexOfInterface)) 1284 return getNextIdentifier(indexOfClass + "class".length()); 1285 return getNextIdentifier(indexOfInterface + "interface".length()); 1286 } 1287 1288 1290 public String getMainClassName() throws ClassNameNotFoundException { 1291 acquireReadLock(); 1292 synchronized(_reduced) { 1293 final int oldLocation = _currentLocation; 1294 1295 try { 1296 setCurrentLocation(0); 1297 final String text = getText(); 1298 1299 final int indexOfClass = _findKeywordAtToplevel("class", text, 0); 1300 final int indexOfInterface = _findKeywordAtToplevel("interface", text, 0); 1301 final int indexOfPublic = _findKeywordAtToplevel("public", text, 0); 1302 1303 if (indexOfPublic == -1) return getFirstClassName(indexOfClass, indexOfInterface); 1304 1305 1308 final int afterPublic = indexOfPublic + "public".length(); 1310 final String subText = text.substring(afterPublic); 1311 setCurrentLocation(afterPublic); 1312 int indexOfPublicClass = _findKeywordAtToplevel("class", subText, afterPublic); if (indexOfPublicClass != -1) indexOfPublicClass += afterPublic; 1315 int indexOfPublicInterface = _findKeywordAtToplevel("interface", subText, afterPublic); if (indexOfPublicInterface != -1) indexOfPublicInterface += afterPublic; 1317 1319 return getFirstClassName(indexOfPublicClass, indexOfPublicInterface); 1320 1321 } 1322 finally { 1323 setCurrentLocation(oldLocation); 1324 releaseReadLock(); 1325 } 1326 } 1327 } 1328 1329 1334 public String getFirstTopLevelClassName() throws ClassNameNotFoundException { 1335 return getNextTopLevelClassName(0, getLength()); 1336 } 1337 1338 public String getNextTopLevelClassName(int startPos, int endPos) throws ClassNameNotFoundException { 1340 1341 acquireReadLock(); 1342 synchronized(_reduced) { 1343 int oldLocation = _currentLocation; 1344 1345 try { 1346 setCurrentLocation(startPos); 1347 final int textLength = endPos - startPos; 1348 final String text = getText(startPos, textLength); 1349 1350 int index; 1351 1352 int indexOfClass = _findKeywordAtToplevel("class", text, startPos); 1353 int indexOfInterface = _findKeywordAtToplevel("interface", text, startPos); 1354 int indexOfEnum = _findKeywordAtToplevel("enum",text,startPos); 1355 1356 if (indexOfClass > -1 && (indexOfInterface <= -1 || indexOfClass < indexOfInterface) 1359 && (indexOfEnum <= -1 || indexOfClass < indexOfEnum)) { 1360 index = indexOfClass + "class".length(); 1361 } 1362 else if (indexOfInterface > -1 && (indexOfClass <= -1 || indexOfInterface < indexOfClass) 1363 && (indexOfEnum <= -1 || indexOfInterface < indexOfEnum)) { 1364 index = indexOfInterface + "interface".length(); 1365 } 1366 else if (indexOfEnum > -1 && (indexOfClass <= -1 || indexOfEnum < indexOfClass) 1367 && (indexOfInterface <= -1 || indexOfEnum < indexOfInterface)) { 1368 index = indexOfEnum + "enum".length(); 1369 } 1370 else { 1371 throw ClassNameNotFoundException.DEFAULT; 1373 } 1374 1375 return getNextIdentifier(startPos + index); 1377 } 1378 catch (BadLocationException ble) { throw new UnexpectedException(ble); } 1379 catch (IllegalStateException e) { throw new ClassNameNotFoundException("No top level class name found"); } 1380 finally { 1381 setCurrentLocation(oldLocation); 1382 releaseReadLock(); 1383 } 1384 } 1385 } 1386 1387 1389 private String getNextIdentifier(final int startPos) throws ClassNameNotFoundException { 1390 1391 1393 try { 1399 int index = getFirstNonWSCharPos(startPos); 1401 if (index == -1) throw new IllegalStateException ("No identifier found"); 1402 1403 String text = getText(); 1404 int length = text.length(); 1405 int endIndex = length; 1407 1410 char c; 1412 for (int i = index; i < length; i++) { 1413 c = text.charAt(i); 1414 if (! Character.isJavaIdentifierPart(c)) { 1415 endIndex = i; 1416 break; 1417 } 1418 } 1419 return text.substring(index, endIndex); 1421 } 1422 catch(BadLocationException e) { 1423 throw new UnexpectedException(e); 1427 } 1428 } 1429 1430 1437 private int _findKeywordAtToplevel(String keyword, String text, int textOffset) { 1438 1439 acquireReadLock(); 1440 synchronized(_reduced) { 1441 int oldLocation = _currentLocation; 1442 int index = 0; 1443 try { 1444 while (true) { 1445 index = text.indexOf(keyword, index); 1446 if (index == -1) break; else { 1448 setCurrentLocation(textOffset + index); 1450 1451 ReducedToken rt = _reduced.currentToken(); 1453 int indexPastKeyword = index + keyword.length(); 1454 if (indexPastKeyword < text.length()) { 1455 if (rt.getState() == ReducedModelStates.FREE && 1456 Character.isWhitespace(text.charAt(indexPastKeyword))) { 1457 if (! posNotInBlock(index)) index = -1; break; 1460 } 1461 else index++; } 1463 else { index = -1; 1465 break; 1466 } 1467 } 1468 } 1469 setCurrentLocation(oldLocation); 1470 return index; 1472 } 1473 finally { releaseReadLock(); } 1474 } 1475 } 1476 1477 1478 public static class WrappedPosition implements Position { 1480 private Position _wrapped; 1481 public WrappedPosition(Position w) { setWrapped(w); } 1482 public void setWrapped(Position w) { _wrapped = w; } 1483 public int getOffset() { return _wrapped.getOffset(); } 1484 } 1485 1486 1488 public Position createPosition(int offs) throws BadLocationException { 1489 WrappedPosition wp = new WrappedPosition(createUnwrappedPosition(offs)); 1490 synchronized(_wrappedPosListLock) { 1491 if (_wrappedPosList == null) _wrappedPosList = new LinkedList <WeakReference <WrappedPosition>>(); 1492 _wrappedPosList.add(new WeakReference <WrappedPosition>(wp)); 1493 } 1494 return wp; 1495 } 1496 1497 1501 public WeakHashMap <WrappedPosition, Integer > getWrappedPositionOffsets() { 1502 LinkedList <WeakReference <WrappedPosition>> newList = new LinkedList <WeakReference <WrappedPosition>>(); 1503 synchronized(_wrappedPosListLock) { 1504 if (_wrappedPosList == null) { _wrappedPosList = new LinkedList <WeakReference <WrappedPosition>>(); } 1505 WeakHashMap <WrappedPosition, Integer > ret = new WeakHashMap <WrappedPosition, Integer >(_wrappedPosList.size()); 1506 1507 for (WeakReference <WrappedPosition> wr: _wrappedPosList) { 1508 if (wr.get() != null) { 1509 newList.add(wr); 1511 ret.put(wr.get(), wr.get().getOffset()); 1512 } 1513 } 1514 _wrappedPosList.clear(); 1515 _wrappedPosList = newList; 1516 return ret; 1517 } 1518 } 1519 1520 1523 public void setWrappedPositionOffsets(WeakHashMap <WrappedPosition, Integer > whm) throws BadLocationException { 1524 synchronized(_wrappedPosListLock) { 1525 if (_wrappedPosList == null) { _wrappedPosList = new LinkedList <WeakReference <WrappedPosition>>(); } 1526 _wrappedPosList.clear(); 1527 1528 for(Map.Entry <WrappedPosition, Integer > entry: whm.entrySet()) { 1529 if (entry.getKey() != null) { 1530 WrappedPosition wp = entry.getKey(); 1532 wp.setWrapped(createUnwrappedPosition(entry.getValue())); 1533 _wrappedPosList.add(new WeakReference <WrappedPosition>(wp)); 1534 } 1535 } 1536 } 1537 } 1538 1539 1540 private static class CommandUndoableEdit extends AbstractUndoableEdit { 1541 private final Runnable _undoCommand; 1542 private final Runnable _redoCommand; 1543 1544 public CommandUndoableEdit(final Runnable undoCommand, final Runnable redoCommand) { 1545 _undoCommand = undoCommand; 1546 _redoCommand = redoCommand; 1547 } 1548 1549 public void undo() throws CannotUndoException { 1550 super.undo(); 1551 _undoCommand.run(); 1552 } 1553 1554 public void redo() throws CannotRedoException { 1555 super.redo(); 1556 _redoCommand.run(); 1557 } 1558 1559 public boolean isSignificant() { return false; } 1560 } 1561 1562 1566 public CompoundUndoManager getUndoManager() { return _undoManager; } 1567 1568 1569 public void resetUndoManager() { 1570 _undoManager = new CompoundUndoManager(_notifier); 1571 _undoManager.setLimit(UNDO_LIMIT); 1572 } 1573 1574 1575 public UndoableEdit getNextUndo() { return _undoManager.getNextUndo(); } 1576 1577 1578 public UndoableEdit getNextRedo() { return _undoManager.getNextRedo(); } 1579 1580 1581 public void documentSaved() { _undoManager.documentSaved(); } 1582 1583 protected int startCompoundEdit() { return _undoManager.startCompoundEdit(); } 1584 1585 protected void endCompoundEdit(int key) { 1586 _undoManager.endCompoundEdit(key); 1587 } 1588 1589 protected void endLastCompoundEdit() { _undoManager.endLastCompoundEdit(); } 1591 1592 protected void addUndoRedo(AbstractDocument.DefaultDocumentEvent chng, Runnable undoCommand, Runnable doCommand) { 1593 chng.addEdit(new CommandUndoableEdit(undoCommand, doCommand)); 1594 } 1595 1596 1597 1601 1652 1653 1656 1675 1676 private List <FinalizationListener<DefinitionsDocument>> _finalizationListeners = 1677 new LinkedList <FinalizationListener<DefinitionsDocument>>(); 1678 1679 1686 public void addFinalizationListener(FinalizationListener<DefinitionsDocument> fl) { 1687 synchronized(_finalizationListeners) { _finalizationListeners.add(fl); } 1688 } 1689 1690 public List <FinalizationListener<DefinitionsDocument>> getFinalizationListeners() { 1691 return _finalizationListeners; 1692 } 1693 1694 1697 protected void finalize() { 1698 FinalizationEvent<DefinitionsDocument> fe = new FinalizationEvent<DefinitionsDocument>(this); 1699 synchronized(_finalizationListeners) { 1700 for (FinalizationListener<DefinitionsDocument> fl: _finalizationListeners) { 1701 fl.finalized(fe); 1702 } 1703 } 1704 } 1705 1706 public String toString() { return "ddoc for " + _odd; } 1707} 1708 | Popular Tags |