1 33 34 package edu.rice.cs.drjava.model; 35 36 import edu.rice.cs.drjava.DrJava; 37 import edu.rice.cs.drjava.config.OptionConstants; 38 import edu.rice.cs.drjava.config.OptionEvent; 39 import edu.rice.cs.drjava.config.OptionListener; 40 41 import edu.rice.cs.drjava.model.definitions.DefinitionsDocument; 42 import edu.rice.cs.drjava.model.definitions.indent.Indenter; 43 import edu.rice.cs.drjava.model.definitions.reducedmodel.BraceReduction; 44 import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelControl; 45 import edu.rice.cs.drjava.model.definitions.reducedmodel.HighlightStatus; 46 import edu.rice.cs.drjava.model.definitions.reducedmodel.IndentInfo; 47 import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelState; 48 import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException; 49 50 51 import edu.rice.cs.util.OperationCanceledException; 52 import edu.rice.cs.util.UnexpectedException; 53 import edu.rice.cs.util.swing.Utilities; 54 import edu.rice.cs.util.text.SwingDocument; 55 56 import java.util.HashSet ; 57 import java.util.Hashtable ; 58 import java.util.StringTokenizer ; 59 import java.util.Vector ; 60 import javax.swing.ProgressMonitor ; 61 import javax.swing.text.AbstractDocument ; 62 import javax.swing.text.AttributeSet ; 63 import javax.swing.text.BadLocationException ; 64 import javax.swing.text.Position ; 65 66 67 public abstract class AbstractDJDocument extends SwingDocument implements DJDocument, OptionConstants { 68 69 70 71 72 protected static final HashSet <String > _normEndings = _makeNormEndings(); 73 74 protected static final HashSet <String > _keywords = _makeKeywords(); 75 76 protected static final HashSet <String > _primTypes = _makePrimTypes(); 77 78 protected volatile int _indent = 2; 79 80 84 public final BraceReduction _reduced = new ReducedModelControl(); 86 87 protected volatile int _currentLocation = 0; 88 89 92 93 96 private final Hashtable <String , Object > _helperCache = new Hashtable <String , Object >(); 97 98 103 private final Vector <String > _helperCacheHistory = new Vector <String >(); 104 105 108 protected volatile boolean _cacheInUse; 109 110 113 private static final int MAX_CACHE_SIZE = 10000; 114 115 116 public static final int DOCSTART = 0; 117 118 119 public static final int ERROR_INDEX = -1; 120 121 122 private volatile Indenter _indenter; 123 124 125 private volatile OptionListener<Integer > _listener1; 126 private volatile OptionListener<Boolean > _listener2; 127 128 129 130 131 protected AbstractDJDocument() { 132 } 136 137 138 protected AbstractDJDocument(Indenter indent) { _indenter = indent; } 139 140 142 143 144 145 protected abstract Indenter makeNewIndenter(int indentLevel); 146 147 150 private Indenter getIndenter() { 151 if (_indenter == null) { 152 int ind = DrJava.getConfig().getSetting(INDENT_LEVEL).intValue(); 153 _indenter = makeNewIndenter(ind); _initNewIndenter(); 155 } 156 return _indenter; 157 } 158 159 162 public int getIndent() { return _indent; } 163 164 167 public void setIndent(final int indent) { 168 DrJava.getConfig().setSetting(INDENT_LEVEL, indent); 169 this._indent = indent; 170 } 171 172 protected void _removeIndenter() { 173 DrJava.getConfig().removeOptionListener(INDENT_LEVEL, _listener1); 174 DrJava.getConfig().removeOptionListener(AUTO_CLOSE_COMMENTS, _listener2); 175 } 176 177 178 private void _initNewIndenter() { 179 181 final Indenter indenter = _indenter; 182 183 _listener1 = new OptionListener<Integer >() { 184 public void optionChanged(OptionEvent<Integer > oce) { 185 indenter.buildTree(oce.value.intValue()); 186 } 187 }; 188 189 _listener2 = new OptionListener<Boolean >() { 190 public void optionChanged(OptionEvent<Boolean > oce) { 191 indenter.buildTree(DrJava.getConfig().getSetting(INDENT_LEVEL).intValue()); 192 } 193 }; 194 195 DrJava.getConfig().addOptionListener(INDENT_LEVEL, _listener1); 196 DrJava.getConfig().addOptionListener(AUTO_CLOSE_COMMENTS, _listener2); 197 } 198 199 200 203 protected static HashSet <String > _makeNormEndings() { 204 HashSet <String > normEndings = new HashSet <String >(); 205 normEndings.add(";"); 206 normEndings.add("{"); 207 normEndings.add("}"); 208 normEndings.add("("); 209 return normEndings; 210 } 211 212 215 protected static HashSet <String > _makeKeywords() { 216 final String [] words = { 217 "import", "native", "package", "goto", "const", "if", "else", "switch", "while", "for", "do", "true", "false", 218 "null", "this", "super", "new", "instanceof", "return", "static", "synchronized", "transient", "volatile", 219 "final", "strictfp", "throw", "try", "catch", "finally", "throws", "extends", "implements", "interface", "class", 220 "break", "continue", "public", "protected", "private", "abstract", "case", "default", "assert", "enum" 221 }; 222 HashSet <String > keywords = new HashSet <String >(); 223 for (int i = 0; i < words.length; i++) { keywords.add(words[i]); } 224 return keywords; 225 } 226 227 230 protected static HashSet <String > _makePrimTypes() { 231 final String [] words = { 232 "boolean", "char", "byte", "short", "int", "long", "float", "double", "void", 233 }; 234 HashSet <String > prims = new HashSet <String >(); 235 for (String w: words) { prims.add(w); } 236 return prims; 237 } 238 239 242 public Vector <HighlightStatus> getHighlightStatus(int start, int end) { 243 244 if (start == end) return new Vector <HighlightStatus>(0); 245 Vector <HighlightStatus> v; 246 247 acquireReadLock(); 248 try { 249 synchronized(_reduced) { 250 setCurrentLocation(start); 251 252 v = _reduced.getHighlightStatus(start, end - start); 253 254 255 for (int i = 0; i < v.size(); i++) { 256 HighlightStatus stat = v.get(i); 257 if (stat.getState() == HighlightStatus.NORMAL) i = _highlightKeywords(v, i); 258 } 259 } 260 } 261 finally { releaseReadLock(); } 262 263 return v; 271 } 272 273 282 private int _highlightKeywords(Vector <HighlightStatus> v, int i) { 283 final String delimiters = " \t\n\r{}()[].+-/*;:=!@#$%^&*~<>?,\"`'<>|"; 285 final HighlightStatus original = v.get(i); 286 final String text; 287 288 try { text = getText(original.getLocation(), original.getLength()); } 289 catch (BadLocationException e) { throw new UnexpectedException(e); } 290 291 StringTokenizer tokenizer = new StringTokenizer (text, delimiters, true); 294 295 int start = original.getLocation(); 297 int length = 0; 298 299 v.remove(i); 301 302 int index = i; 304 305 boolean process; 306 int state = 0; 307 while (tokenizer.hasMoreTokens()) { 308 String token = tokenizer.nextToken(); 309 310 process = false; 312 if (_isType(token)) { 313 state = HighlightStatus.TYPE; 315 process = true; 316 } 317 else if (_keywords.contains(token)) { 318 state = HighlightStatus.KEYWORD; 319 process = true; 320 } 321 else if (_isNum(token)) { 322 state = HighlightStatus.NUMBER; 323 process = true; 324 } 325 326 if (process) { 327 if (length != 0) { 329 HighlightStatus newStat = new HighlightStatus(start, length, original.getState()); 330 v.add(index, newStat); 331 index++; 332 start += length; 333 length = 0; 334 } 335 336 int keywordLength = token.length(); 338 v.add(index, new HighlightStatus(start, keywordLength, state)); 339 index++; 340 start += keywordLength; 342 } 343 else { 344 length += token.length(); 346 } 347 } 348 if (length != 0) { 350 HighlightStatus newStat = new HighlightStatus(start, length, original.getState()); 351 v.add(index, newStat); 352 index++; 353 length = 0; 354 } 355 return index - 1; 357 } 358 359 360 363 private boolean _isNum(String x) { 364 try { 365 Double.parseDouble(x); 366 return true; 367 } 368 catch (NumberFormatException e) { return false; } 369 } 370 371 374 private boolean _isType(String x) { 375 if (_primTypes.contains(x)) return true; 376 377 try { return Character.isUpperCase(x.charAt(0)); } 378 catch (IndexOutOfBoundsException e) { return false; } 379 } 380 381 382 private boolean _hasOnlySpaces(String text) { return (text.trim().length() == 0); } 383 384 400 protected abstract void _styleChanged(); 401 402 406 protected void clearCache() { 407 synchronized(_helperCache) { if (_cacheInUse) _clearCache(); } 408 } 409 410 411 private void _clearCache() { 412 _helperCache.clear(); 413 _helperCacheHistory.clear(); 414 _cacheInUse = false; 415 } 416 417 419 private void _addCharToReducedModel(char curChar) { 420 clearCache(); 421 _reduced.insertChar(curChar); 422 } 423 424 429 public int getCurrentLocation() { return _currentLocation; } 430 431 434 public void setCurrentLocation(int loc) { 435 acquireReadLock(); 436 try { 437 synchronized(_reduced) { 438 int dist = loc - _currentLocation; _currentLocation = loc; 440 _reduced.move(dist); 441 } 442 } 443 finally { releaseReadLock(); } 444 } 445 446 447 450 public void move(int dist) { 451 acquireReadLock(); 452 try { 453 synchronized(_reduced) { 454 int newLoc = _currentLocation + dist; 455 _currentLocation = newLoc; 471 _reduced.move(dist); 472 } 473 } 474 finally { releaseReadLock(); } 475 } 476 477 481 public int balanceBackward() { 482 acquireReadLock(); 483 try { 484 synchronized(_reduced) { return _reduced.balanceBackward(); } 485 } 486 finally { releaseReadLock(); } 487 } 488 489 493 public int balanceForward() { 494 acquireReadLock(); 495 try { synchronized(_reduced) { return _reduced.balanceForward(); } } 496 finally { releaseReadLock(); } 497 } 498 499 502 public BraceReduction getReduced() { return _reduced; } 503 504 505 public IndentInfo getIndentInformation() { 506 String key = "getIndentInformation:" + _currentLocation; 508 509 IndentInfo cached = (IndentInfo) _checkCache(key); 510 if (cached != null) return cached; 511 512 IndentInfo info; 513 acquireReadLock(); 514 try { 515 synchronized(_reduced) { info = _reduced.getIndentInformation(); } 516 _storeInCache(key, info); 517 } 518 finally { releaseReadLock(); } 519 520 return info; 521 } 522 523 public ReducedModelState stateAtRelLocation(int dist) { 524 acquireReadLock(); 525 try { synchronized(_reduced) { return _reduced.moveWalkerGetState(dist); } } 526 finally { releaseReadLock(); } 527 } 528 529 public ReducedModelState getStateAtCurrent() { 530 acquireReadLock(); 531 try { synchronized(_reduced) { return _reduced.getStateAtCurrent(); } } 532 finally { releaseReadLock(); } 533 } 534 535 public void resetReducedModelLocation() { 536 acquireReadLock(); 537 try { synchronized(_reduced) { _reduced.resetLocation(); } } 538 finally { releaseReadLock(); } 539 } 540 541 548 public int findPrevEnclosingBrace(int pos, char opening, char closing) throws BadLocationException { 549 final StringBuilder keyBuf = 551 new StringBuilder ("findPrevEnclosingBrace:").append(opening).append(':').append(closing).append(':').append(pos); 552 final String key = keyBuf.toString(); 553 final Integer cached = (Integer ) _checkCache(key); 554 if (cached != null) return cached.intValue(); 555 556 if ((pos >= getLength()) || (pos == DOCSTART)) { return ERROR_INDEX; } 557 558 final char[] delims = {opening, closing}; 559 int reducedPos = pos; 560 int i; int braceBalance = 0; 562 563 acquireReadLock(); 564 try { 565 String text = getText(DOCSTART, pos); 566 567 synchronized(_reduced) { 568 final int origLocation = _currentLocation; 569 _reduced.move(pos - origLocation); 572 for (i = pos-1; i >= DOCSTART; i--) { 574 576 577 if (match(text.charAt(i),delims)) { 578 _reduced.move(i - reducedPos); reducedPos = i; 582 ReducedModelState state = _reduced.getStateAtCurrent(); 585 if (!state.equals(ReducedModelState.FREE) || _isStartOfComment(text, i) 586 || ((i > 0) && _isStartOfComment(text, i - 1))) 587 continue; else { 589 if (text.charAt(i)==closing) ++braceBalance; 591 else { 592 if (braceBalance==0) break; --braceBalance; 594 } 595 } 596 } 597 } 598 599 600 601 _reduced.move(origLocation - reducedPos); } 604 if (i == DOCSTART-1) reducedPos = ERROR_INDEX; _storeInCache(key, reducedPos); 606 } 607 finally { releaseReadLock(); } 608 609 return reducedPos; 611 } 612 613 619 public int findNextEnclosingBrace(int pos, char opening, char closing) throws BadLocationException { 620 final StringBuilder keyBuf = 622 new StringBuilder ("findNextEnclosingBrace:").append(opening).append(':').append(closing).append(':').append(pos); 623 final String key = keyBuf.toString(); 624 final Integer cached = (Integer ) _checkCache(key); 625 626 if (cached != null) return cached.intValue(); 627 if (pos>=getLength()-1) { return ERROR_INDEX; } 628 629 final char[] delims = {opening, closing}; 630 int reducedPos = pos; 631 int i; int braceBalance = 0; 633 634 acquireReadLock(); 635 String text = getText(); 636 try { 637 synchronized(_reduced) { 638 final int origLocation = _currentLocation; 639 _reduced.move(pos - origLocation); 642 for (i = pos+1; i < text.length(); i++) { 644 646 647 if (match(text.charAt(i),delims)) { 648 _reduced.move(i - reducedPos); reducedPos = i; 652 ReducedModelState state = _reduced.getStateAtCurrent(); 655 if (!state.equals(ReducedModelState.FREE) || _isStartOfComment(text, i) 656 || ((i > 0) && _isStartOfComment(text, i - 1))) 657 continue; else { 659 if (text.charAt(i)==opening) { 661 ++braceBalance; 662 } 663 else { 664 if (braceBalance==0) break; --braceBalance; 666 } 667 } 668 } 669 } 670 671 672 673 _reduced.move(origLocation - reducedPos); } 676 if (i == text.length()) reducedPos = ERROR_INDEX; _storeInCache(key, reducedPos); 678 } 679 finally { releaseReadLock(); } 680 681 return reducedPos; 683 } 684 685 692 public int findPrevDelimiter(int pos, char[] delims) throws BadLocationException { 693 return findPrevDelimiter(pos, delims, true); 694 } 695 696 703 public int findPrevDelimiter(final int pos, final char[] delims, final boolean skipParenPhrases) 704 throws BadLocationException { 705 final StringBuilder keyBuf = new StringBuilder ("findPrevDelimiter:").append(pos); 707 for (char ch: delims) { keyBuf.append(':').append(ch); } 708 keyBuf.append(':').append(skipParenPhrases); 709 final String key = keyBuf.toString(); 710 final Integer cached = (Integer ) _checkCache(key); 711 if (cached != null) return cached.intValue(); 712 713 int reducedPos = pos; 714 int i; acquireReadLock(); 716 try { 717 String text = getText(DOCSTART, pos); 718 719 synchronized(_reduced) { 720 final int origLocation = _currentLocation; 721 _reduced.move(pos - origLocation); 724 for (i = pos-1; i >= DOCSTART; i--) { 726 728 729 if (match(text.charAt(i),delims)) { 730 _reduced.move(i - reducedPos); reducedPos = i; 734 ReducedModelState state = _reduced.getStateAtCurrent(); 736 if (! state.equals(ReducedModelState.FREE) || _isStartOfComment(text, i) 737 || ((i > 0) && _isStartOfComment(text, i - 1)) || (skipParenPhrases && posInParenPhrase())) 738 continue; else break; } 741 } 742 743 744 745 _reduced.move(origLocation - reducedPos); } 748 if (i == DOCSTART-1) reducedPos = ERROR_INDEX; _storeInCache(key, reducedPos); 750 } 751 finally { releaseReadLock(); } 752 753 return reducedPos; 755 } 756 757 private static boolean match(char c, char[] delims) { 758 for (char d : delims) { if (c == d) return true; } return false; 760 } 761 762 765 public boolean findCharInStmtBeforePos(char findChar, int position) { 766 if (position == DefinitionsDocument.ERROR_INDEX) { 767 String mesg = 768 "Argument endChar to QuestionExistsCharInStmt must be a char that exists on the current line."; 769 throw new UnexpectedException(new IllegalArgumentException (mesg)); 770 } 771 772 char[] findCharDelims = {findChar, ';', '{', '}'}; 773 int prevFindChar; 774 775 boolean found; 777 778 acquireReadLock(); 779 try { 780 prevFindChar = this.findPrevDelimiter(position, findCharDelims, false); 781 782 if ((prevFindChar == DefinitionsDocument.ERROR_INDEX) || (prevFindChar < 0)) return false; 784 String foundString = this.getText(prevFindChar, 1); 786 char foundChar = foundString.charAt(0); 787 found = (foundChar == findChar); 788 } 789 catch (Throwable t) { throw new UnexpectedException(t); } 790 finally { releaseReadLock(); } 791 return found; 792 } 793 794 800 public int findPrevCharPos(int pos, char[] whitespace) throws BadLocationException { 801 final StringBuilder keyBuf = new StringBuilder ("findPrevCharPos:").append(pos); 803 for (char ch: whitespace) { keyBuf.append( ':').append(ch); } 804 final String key = keyBuf.toString(); 805 final Integer cached = (Integer ) _checkCache(key); 806 if (cached != null) return cached.intValue(); 807 808 int reducedPos = pos; 809 int i = pos - 1; 810 String text; 811 acquireReadLock(); 812 try { 813 text = getText(0, pos); 814 815 synchronized(_reduced) { 816 817 final int origLocation = _currentLocation; 818 _reduced.move(pos - origLocation); 821 823 while (i >= 0) { 824 826 827 if (match(text.charAt(i), whitespace)) { 828 i--; 830 continue; 831 } 832 833 _reduced.move(i - reducedPos); 835 reducedPos = i; 837 if ((_reduced.getStateAtCurrent().equals(ReducedModelState.INSIDE_LINE_COMMENT)) || 839 (_reduced.getStateAtCurrent().equals(ReducedModelState.INSIDE_BLOCK_COMMENT))) { 840 i--; 841 continue; 842 } 843 844 if (_isReversteStartOfComment(text, i)) { 845 i = i - 2; 847 continue; 848 } 849 850 break; 852 } 853 854 855 _reduced.move(origLocation - reducedPos); 856 } 857 858 int result = reducedPos; 859 if (i < 0) result = ERROR_INDEX; 860 _storeInCache(key, result); 861 return result; 862 } 863 finally { releaseReadLock(); } 864 865 } 866 867 871 protected Object _checkCache(String key) { 872 Object result = _helperCache.get(key); 874 return result; 876 } 877 878 882 protected void _storeInCache(String key, Object result) { 883 synchronized(_helperCache) { 884 _cacheInUse = true; 885 886 if (_helperCache.size() >= MAX_CACHE_SIZE) { 888 if (_helperCacheHistory.size() > 0) { 889 _helperCache.remove( _helperCacheHistory.get(0) ); 890 _helperCacheHistory.remove(0); 891 } 892 else { throw new RuntimeException ("Cache larger than cache history!"); 894 } 895 } 896 Object prev = _helperCache.put(key, result); 897 if (prev == null) _helperCacheHistory.add(key); 899 } 900 } 901 902 906 public void indentLines(int selStart, int selEnd) { 907 try { indentLines(selStart, selEnd, Indenter.OTHER, null); } 908 catch (OperationCanceledException oce) { 909 throw new UnexpectedException(oce); 911 } 912 } 913 914 915 923 public void indentLines(int selStart, int selEnd, int reason, ProgressMonitor pm) 924 throws OperationCanceledException { 925 926 929 acquireWriteLock(); 930 try { 931 synchronized(_reduced) { 932 if (selStart == selEnd) { Position oldCurrentPosition = createUnwrappedPosition(_currentLocation); 935 936 if (_indentLine(reason)) { 939 setCurrentLocation(oldCurrentPosition.getOffset()); 940 if (onlyWhiteSpaceBeforeCurrent()) { 941 int space = getWhiteSpace(); 942 move(space); 943 } 944 } 945 } 946 else _indentBlock(selStart, selEnd, reason, pm); 947 } 948 } 949 catch (Throwable t) { throw new UnexpectedException(t); } 950 finally { releaseWriteLock(); } 951 952 endLastCompoundEdit(); 955 } 956 957 965 private void _indentBlock(final int start, final int end, int reason, ProgressMonitor pm) 966 throws OperationCanceledException, BadLocationException { 967 968 final Position endPos = this.createUnwrappedPosition(end); 971 int walker = start; 973 while (walker < endPos.getOffset()) { 974 setCurrentLocation(walker); 975 Position walkerPos = this.createUnwrappedPosition(walker); 978 _indentLine(reason); setCurrentLocation(walkerPos.getOffset()); 983 walker = walkerPos.getOffset(); 984 985 if (pm != null) { 986 pm.setProgress(walker); if (pm.isCanceled()) throw new OperationCanceledException(); } 989 990 walker += _reduced.getDistToNextNewline() + 1; 993 } 994 } 995 996 997 public boolean _indentLine(int reason) { return getIndenter().indent(this, reason); } 998 999 1005 public int getIntelligentBeginLinePos(int currPos) throws BadLocationException { 1006 String prefix; 1007 int firstChar; 1008 acquireReadLock(); 1009 try { 1010 firstChar = getLineStartPos(currPos); 1011 prefix = getText(firstChar, currPos-firstChar); 1012 } 1013 finally { releaseReadLock(); } 1014 1015 int i; 1017 int len = prefix.length(); 1018 1019 for (i = 0; i < len; i++ ) { if (! Character.isWhitespace(prefix.charAt(i))) break; } 1020 1021 if (i < len) { 1023 int firstRealChar = firstChar + i; 1024 if (firstRealChar < currPos) return firstRealChar; 1025 } 1026 return firstChar; 1028 } 1029 1030 1034 public String getIndentOfCurrStmt(int pos) throws BadLocationException { 1035 char[] delims = {';', '{', '}'}; 1036 char[] whitespace = {' ', '\t', '\n', ','}; 1037 return getIndentOfCurrStmt(pos, delims, whitespace); 1038 } 1039 1040 1044 public String getIndentOfCurrStmt(int pos, char[] delims) throws BadLocationException { 1045 char[] whitespace = {' ', '\t', '\n',','}; 1046 return getIndentOfCurrStmt(pos, delims, whitespace); 1047 } 1048 1049 1054 public String getIndentOfCurrStmt(int pos, char[] delims, char[] whitespace) throws BadLocationException { 1055 final StringBuilder keyBuf = new StringBuilder ("getIndentOfCurrStmt:").append(pos); 1057 for (char ch: delims) { keyBuf.append(':').append(ch); } 1058 final String key = keyBuf.toString(); 1059 final String cached = (String ) _checkCache(key); 1060 if (cached != null) return cached; 1061 1062 String lineText; 1063 1064 acquireReadLock(); 1065 try { 1066 synchronized(_reduced) { 1067 int lineStart = getLineStartPos(pos); 1069 1070 boolean reachedStart = false; 1072 int prevDelim = lineStart; 1073 boolean ignoreParens = posInParenPhrase(prevDelim); 1074 1075 do { 1076 prevDelim = findPrevDelimiter(prevDelim, delims, ignoreParens); 1077 if (prevDelim > 0 && prevDelim < getLength() && getText(prevDelim,1).charAt(0) == '{') break; 1078 if (prevDelim == ERROR_INDEX) { reachedStart = true; 1080 break; 1081 } 1082 ignoreParens = posInParenPhrase(prevDelim); 1083 } while (ignoreParens); 1084 1085 int nextNonWSChar; 1087 if (reachedStart) nextNonWSChar = getFirstNonWSCharPos(DOCSTART); 1088 else nextNonWSChar = getFirstNonWSCharPos(prevDelim+1, whitespace, false); 1089 1090 if (nextNonWSChar == ERROR_INDEX) nextNonWSChar = getLength(); 1092 1093 int lineStartStmt = getLineStartPos(nextNonWSChar); 1095 1096 int lineFirstNonWS = getLineFirstCharPos(lineStartStmt); 1098 lineText = getText(lineStartStmt, lineFirstNonWS - lineStartStmt); 1099 _storeInCache(key, lineText); 1100 } 1101 } 1102 catch(Throwable t) { throw new UnexpectedException(t); } 1103 finally { releaseReadLock(); } 1104 1105 return lineText; 1106 } 1107 1108 1114 public int findCharOnLine(int pos, char findChar) { 1115 String key = "findCharOnLine:" + pos + ":" + findChar; 1117 Integer cached = (Integer ) _checkCache(key); 1118 if (cached != null) return cached.intValue(); 1119 1120 int i; 1121 int matchIndex; 1123 acquireReadLock(); 1124 try { 1125 synchronized(_reduced) { 1126 int here = _currentLocation; 1127 int lineStart = getLineStartPos(pos); 1128 int lineEnd = getLineEndPos(pos); 1129 String lineText = getText(lineStart, lineEnd - lineStart); 1130 i = lineText.indexOf(findChar, 0); 1131 matchIndex = i + lineStart; 1132 1133 while (i != -1) { 1136 1137 _reduced.move(matchIndex - here); 1140 if (_reduced.getStateAtCurrent().equals(ReducedModelState.FREE)) { 1142 _reduced.move(here - matchIndex); break; 1145 } 1146 1147 _reduced.move(here - matchIndex); i = lineText.indexOf(findChar, i+1); 1150 } 1151 } 1152 1153 if (i == -1) matchIndex = ERROR_INDEX; 1154 _storeInCache(key, matchIndex); 1155 } 1156 catch (Throwable t) { throw new UnexpectedException(t); } 1157 finally { releaseReadLock(); } 1158 1159 return matchIndex; 1160 } 1161 1162 1167 public int getLineStartPos(final int pos) { 1168 if (pos < 0 || pos > getLength()) return -1; 1169 String key = "getLineStartPos:" + pos; 1171 Integer cached = (Integer ) _checkCache(key); 1172 if (cached != null) return cached.intValue(); 1173 1174 int dist; 1175 acquireReadLock(); 1176 try { 1177 synchronized(_reduced) { 1178 int location = _currentLocation; 1179 _reduced.move(pos - location); 1180 dist = _reduced.getDistToPreviousNewline(0); 1181 _reduced.move(location - pos); 1182 } 1183 1184 if (dist == -1) { 1185 _storeInCache(key, DOCSTART); 1187 return DOCSTART; 1188 } 1189 _storeInCache(key, pos - dist); 1190 } 1191 finally { releaseReadLock(); } 1192 1193 return pos - dist; 1194 } 1195 1196 1200 public int getLineEndPos(final int pos) { 1201 if (pos < 0 || pos > getLength()) return -1; 1202 1203 String key = "getLineEndPos:" + pos; 1205 Integer cached = (Integer ) _checkCache(key); 1206 if (cached != null) return cached.intValue(); 1207 1208 int dist; 1209 acquireReadLock(); 1210 try { 1211 synchronized(_reduced) { 1212 int location = _currentLocation; 1213 _reduced.move(pos - location); 1214 dist = _reduced.getDistToNextNewline(); 1215 _reduced.move(location - pos); 1216 } 1217 _storeInCache(key, pos + dist); 1218 } 1219 finally { releaseReadLock(); } 1220 1221 return pos + dist; 1222 } 1223 1224 1230 public int getLineFirstCharPos(int pos) throws BadLocationException { 1231 String key = "getLineFirstCharPos:" + pos; 1233 Integer cached = (Integer ) _checkCache(key); 1234 if (cached != null) return cached.intValue(); 1235 1236 acquireReadLock(); 1237 try { 1238 int startLinePos = getLineStartPos(pos); 1239 int endLinePos = getLineEndPos(pos); 1240 1241 String text = this.getText(startLinePos, endLinePos - startLinePos); 1243 int walker = 0; 1244 while (walker < text.length()) { 1245 if (text.charAt(walker) == ' ' || text.charAt(walker) == '\t') walker++; 1246 else { 1247 _storeInCache(key, startLinePos + walker); 1248 return startLinePos + walker; 1249 } 1250 } 1251 _storeInCache(key, endLinePos); 1253 return endLinePos; 1254 } 1255 finally { releaseReadLock(); } 1256 } 1257 1258 1263 public int getFirstNonWSCharPos(int pos) throws BadLocationException { 1264 char[] whitespace = {' ', '\t', '\n'}; 1265 return getFirstNonWSCharPos(pos, whitespace, false); 1266 } 1267 1268 1274 public int getFirstNonWSCharPos(int pos, boolean acceptComments) throws BadLocationException { 1275 char[] whitespace = {' ', '\t', '\n'}; 1276 return getFirstNonWSCharPos(pos, whitespace, acceptComments); 1277 } 1278 1279 1286 public int getFirstNonWSCharPos(int pos, char[] whitespace, boolean acceptComments) throws BadLocationException { 1287 final StringBuilder keyBuf = new StringBuilder ("getFirstNonWSCharPos:").append(pos); 1289 for (char ch: whitespace) { keyBuf.append(':').append(ch); } 1290 final String key = keyBuf.toString(); 1291 1292 final Integer cached = (Integer ) _checkCache(key); 1293 if (cached != null) return cached.intValue(); 1294 1295 int result = ERROR_INDEX; 1297 acquireReadLock(); 1298 try { 1299 1300 int i = pos; 1301 int endPos = getLength(); 1302 1303 String text = getText(pos, endPos - pos); 1305 1306 final int origLocation = _currentLocation; 1307 synchronized(_reduced) { 1309 _reduced.move(pos - origLocation); 1310 int reducedPos = pos; 1311 1312 1314 while (i < endPos) { 1316 1317 if (match(text.charAt(i-pos), whitespace)) { 1319 i++; 1320 continue; 1321 } 1322 _reduced.move(i - reducedPos); reducedPos = i; 1327 if (! acceptComments && 1329 ((_reduced.getStateAtCurrent().equals(ReducedModelState.INSIDE_LINE_COMMENT)) || 1330 (_reduced.getStateAtCurrent().equals(ReducedModelState.INSIDE_BLOCK_COMMENT)))) { 1331 i++; 1332 continue; 1333 } 1334 1335 if (! acceptComments && _isStartOfComment(text, i - pos)) { 1337 i = i + 2; 1340 continue; 1341 } 1342 1343 break; 1345 } 1346 _reduced.move(origLocation - reducedPos); 1347 1348 result = reducedPos; 1349 if (i == endPos) result = ERROR_INDEX; 1350 } 1351 _storeInCache(key, result); 1352 } 1353 finally { releaseReadLock(); } 1354 1355 return result; 1356 } 1357 1358 public int findPrevNonWSCharPos(int pos) throws BadLocationException { 1359 char[] whitespace = {' ', '\t', '\n'}; 1360 return findPrevCharPos(pos, whitespace); 1361 } 1362 1363 1366 protected static boolean _isStartOfComment(String text, int pos) { 1367 char currChar = text.charAt(pos); 1368 if (currChar == '/') { 1369 try { 1370 char afterCurrChar = text.charAt(pos + 1); 1371 if ((afterCurrChar == '/') || (afterCurrChar == '*')) return true; 1372 } catch (StringIndexOutOfBoundsException e) { } 1373 } 1374 return false; 1375 } 1376 1377 1381 protected static boolean _isReversteStartOfComment(String text, int pos) { 1382 char currChar = text.charAt(pos); 1383 if ((currChar == '/')||(currChar == '*')) { 1384 try { 1385 char beforeCurrChar = text.charAt(pos - 1); 1386 if (beforeCurrChar == '/') return true; 1387 } catch (StringIndexOutOfBoundsException e) { } 1388 } 1389 return false; 1390 } 1391 1392 1393 1397 public boolean posInParenPhrase(int pos) { 1398 String key = "posInParenPhrase:" + pos; 1400 Boolean cached = (Boolean ) _checkCache(key); 1401 if (cached != null) return cached.booleanValue(); 1402 1403 boolean inParenPhrase; 1404 1405 acquireReadLock(); 1406 try { 1407 synchronized(_reduced) { 1408 int here = _currentLocation; 1409 _reduced.move(pos - here); 1410 inParenPhrase = posInParenPhrase(); 1411 _reduced.move(here - pos); 1412 } 1413 _storeInCache(key, Boolean.valueOf(inParenPhrase)); 1414 } 1415 finally { releaseReadLock(); } 1416 1417 return inParenPhrase; 1418 } 1419 1420 1424 public boolean posInParenPhrase() { 1425 IndentInfo info; 1426 acquireReadLock(); 1427 try { synchronized(_reduced) { info = _reduced.getIndentInformation(); } } 1428 finally { releaseReadLock(); } 1429 return info.braceTypeCurrent.equals(IndentInfo.openParen); 1430 } 1431 1432 1436 protected boolean posNotInBlock(int pos) { 1437 String key = "posNotInBlock:" + pos; 1439 Boolean cached = (Boolean ) _checkCache(key); 1440 if (cached != null) return cached.booleanValue(); 1441 1442 boolean notInParenPhrase; 1443 1444 synchronized(_reduced) { 1445 int here = _currentLocation; 1446 _reduced.move(pos - here); 1447 IndentInfo info = _reduced.getIndentInformation(); 1448 notInParenPhrase = info.braceTypeCurrent.equals(IndentInfo.noBrace); 1449 _reduced.move(here - pos); 1450 } 1451 _storeInCache(key, Boolean.valueOf(notInParenPhrase)); 1452 return notInParenPhrase; 1453 } 1454 1455 1456 1460 public int getWhiteSpace() { 1461 try { return getWhiteSpaceBetween(0, getLength() - _currentLocation); } 1462 catch (BadLocationException e) { e.printStackTrace(); } 1463 return -1; 1464 } 1465 1466 1471 private int getWhiteSpaceBetween(int relStart, int relEnd) throws BadLocationException { 1472 String text = this.getText(_currentLocation - relStart, Math.abs(relStart - relEnd)); 1473 int i = 0; 1474 int length = text.length(); 1475 while ((i < length) && (text.charAt(i) == ' ')) 1476 i++; 1477 return i; 1478 } 1479 1480 1485 private boolean onlyWhiteSpaceBeforeCurrent() throws BadLocationException { 1486 String text = this.getText(0, _currentLocation); 1487 text = text.substring(text.lastIndexOf("\n")+1); 1489 1490 int index = text.length()-1; 1492 char lastChar = ' '; 1493 while(lastChar == ' ' && index >= 0){ 1494 lastChar = text.charAt(index); 1495 index--; 1496 } 1497 1498 if (index < 0) return true; 1499 return false; 1500 } 1501 1502 1503 1504 1508 public void setTab(String tab, int pos) { 1509 try { 1510 int startPos = getLineStartPos(pos); 1511 int firstNonWSPos = getLineFirstCharPos(pos); 1512 int len = firstNonWSPos - startPos; 1513 1514 boolean onlySpaces = _hasOnlySpaces(tab); 1516 if (!onlySpaces || (len != tab.length())) { 1517 1518 if (onlySpaces) { 1519 int diff = tab.length() - len; 1521 if (diff > 0) { 1522 insertString(firstNonWSPos, tab.substring(0, diff), null); 1523 } 1524 else { 1525 remove(firstNonWSPos + diff, -diff); 1526 } 1527 } 1528 else { 1529 remove(startPos, len); 1531 insertString(startPos, tab, null); 1532 } 1533 } 1534 } 1535 catch (BadLocationException e) { 1536 throw new UnexpectedException(e); 1538 } 1539 } 1540 1541 1550 protected void insertUpdate(AbstractDocument.DefaultDocumentEvent chng, AttributeSet attr) { 1551 clearCache(); 1553 1554 super.insertUpdate(chng, attr); 1555 1556 try { 1557 final int offset = chng.getOffset(); 1558 final int length = chng.getLength(); 1559 final String str = getText(offset, length); 1560 1561 InsertCommand doCommand = new InsertCommand(offset, str); 1562 RemoveCommand undoCommand = new RemoveCommand(offset, length); 1563 1564 addUndoRedo(chng,undoCommand,doCommand); 1566 doCommand.run(); 1569 } 1570 catch (BadLocationException ble) { 1571 throw new UnexpectedException(ble); 1572 } 1573 } 1574 1575 1582 protected void removeUpdate(AbstractDocument.DefaultDocumentEvent chng) { 1583 clearCache(); 1584 1585 try { 1586 final int offset = chng.getOffset(); 1587 final int length = chng.getLength(); 1588 final String removedText = getText(offset, length); 1589 super.removeUpdate(chng); 1590 1591 Runnable doCommand = new RemoveCommand(offset, length); 1592 Runnable undoCommand = new InsertCommand(offset, removedText); 1593 1594 addUndoRedo(chng,undoCommand,doCommand); 1596 doCommand.run(); 1598 } 1599 catch (BadLocationException e) { throw new UnexpectedException(e); } 1600 } 1601 1602 1603 1606 public void insertString(int offset, String str, AttributeSet a) throws BadLocationException { 1607 1608 acquireWriteLock(); 1609 try { 1610 synchronized(_reduced) { clearCache(); super.insertString(offset, str, a); 1613 } 1614 } 1615 finally { releaseWriteLock(); } 1616 } 1617 1618 1621 public void remove(final int offset, final int len) throws BadLocationException { 1622 1623 acquireWriteLock(); 1624 try { 1625 synchronized(_reduced) { 1626 clearCache(); super.remove(offset, len); 1628 }; 1629 } 1630 finally { releaseWriteLock(); } 1631 } 1632 1633 public String getText() { 1634 acquireReadLock(); 1635 try { return getText(0, getLength()); } 1636 catch(BadLocationException e) { throw new UnexpectedException(e); } 1637 finally { releaseReadLock(); } 1638 } 1639 1640 1641 public byte[] getBytes() { return getText().getBytes(); } 1642 1643 public void clear() { 1644 acquireWriteLock(); 1645 try { remove(0, getLength()); } 1646 catch(BadLocationException e) { throw new UnexpectedException(e); } 1647 finally { releaseWriteLock(); } 1648 } 1649 1650 protected abstract int startCompoundEdit(); 1652 protected abstract void endCompoundEdit(int i); 1653 protected abstract void endLastCompoundEdit(); 1654 protected abstract void addUndoRedo(AbstractDocument.DefaultDocumentEvent chng, Runnable undoCommand, Runnable doCommand); 1655 1656 1658 1660 protected class InsertCommand implements Runnable { 1661 private final int _offset; 1662 private final String _text; 1663 1664 public InsertCommand(final int offset, final String text) { 1665 _offset = offset; 1666 _text = text; 1667 } 1668 1669 public void run() { 1670 acquireReadLock(); 1672 try { 1673 synchronized(_reduced) { 1674 _reduced.move(_offset - _currentLocation); 1675 int len = _text.length(); 1676 for (int i = 0; i < len; i++) { 1678 char curChar = _text.charAt(i); 1679 _addCharToReducedModel(curChar); 1680 } 1681 _currentLocation = _offset + len; _styleChanged(); 1683 } 1684 } 1685 finally { releaseReadLock(); } 1686 } 1687 } 1688 1689 protected class RemoveCommand implements Runnable { 1690 private final int _offset; 1691 private final int _length; 1692 1693 public RemoveCommand(final int offset, final int length) { 1694 _offset = offset; 1695 _length = length; 1696 } 1697 1698 public void run() { 1699 acquireReadLock(); 1700 try { 1701 synchronized(_reduced) { 1702 setCurrentLocation(_offset); 1703 _reduced.delete(_length); 1704 _styleChanged(); 1705 } 1706 } 1707 finally { releaseReadLock(); } 1708 } 1709 } 1710} 1711 | Popular Tags |