1 23 24 package org.gjt.sp.jedit.buffer; 25 26 import javax.swing.*; 28 import javax.swing.text.*; 29 import java.awt.Toolkit ; 30 import java.lang.reflect.*; 31 import java.util.*; 32 import java.util.regex.Pattern ; 33 import org.gjt.sp.jedit.jEdit; 34 import org.gjt.sp.jedit.Debug; 35 import org.gjt.sp.jedit.Mode; 36 import org.gjt.sp.jedit.TextUtilities; 37 import org.gjt.sp.jedit.indent.*; 38 import org.gjt.sp.jedit.syntax.*; 39 import org.gjt.sp.jedit.textarea.*; 40 import org.gjt.sp.util.*; 41 43 64 public class JEditBuffer 65 { 66 69 public static final String LINESEP = "lineSeparator"; 70 71 75 public static final String ENCODING = "encoding"; 76 77 public JEditBuffer(Map props) 79 { 80 bufferListeners = new Vector<Listener>(); 81 lock = new ReadWriteLock(); 82 contentMgr = new ContentManager(); 83 lineMgr = new LineManager(); 84 positionMgr = new PositionManager(this); 85 undoMgr = new UndoManager(this); 86 seg = new Segment(); 87 integerArray = new IntegerArray(); 88 propertyLock = new Object (); 89 properties = new HashMap<Object , PropValue>(); 90 91 Set<Map.Entry> set = props.entrySet(); 93 for (Map.Entry entry : set) 94 { 95 properties.put(entry.getKey(),new PropValue(entry.getValue(),false)); 96 } 98 if(getProperty(ENCODING) == null) 101 properties.put(ENCODING,new PropValue(System.getProperty("file.encoding"),false)); 102 if(getProperty(LINESEP) == null) 103 properties.put(LINESEP,new PropValue(System.getProperty("line.separator"),false)); 104 } 106 public JEditBuffer() 108 { 109 bufferListeners = new Vector<Listener>(); 110 lock = new ReadWriteLock(); 111 contentMgr = new ContentManager(); 112 lineMgr = new LineManager(); 113 positionMgr = new PositionManager(this); 114 undoMgr = new UndoManager(this); 115 seg = new Segment(); 116 integerArray = new IntegerArray(); 117 propertyLock = new Object (); 118 properties = new HashMap<Object , PropValue>(); 119 120 properties.put("wrap",new PropValue("none",false)); 121 122 TokenMarker tokenMarker = new TokenMarker(); 123 tokenMarker.addRuleSet(new ParserRuleSet("text","MAIN")); 124 setTokenMarker(tokenMarker); 125 126 loadText(null,null); 127 if(getProperty(ENCODING) == null) 129 properties.put(ENCODING,new PropValue(System.getProperty("file.encoding"),false)); 130 if(getProperty(LINESEP) == null) 131 properties.put(LINESEP,new PropValue(System.getProperty("line.separator"),false)); 132 133 setFoldHandler(new DummyFoldHandler()); 134 } 136 138 143 public boolean isDirty() 144 { 145 return dirty; 146 } 148 public boolean isLoading() 150 { 151 return loading; 152 } 154 public void setLoading(boolean loading) 156 { 157 this.loading = loading; 158 } 160 166 public boolean isPerformingIO() 167 { 168 return isLoading() || io; 169 } 171 177 public void setPerformingIO(boolean io) 178 { 179 this.io = io; 180 } 182 189 public boolean isEditable() 190 { 191 return !(isReadOnly() || isPerformingIO()); 192 } 194 199 public boolean isReadOnly() 200 { 201 return readOnly || readOnlyOverride; 202 } 204 209 public void setReadOnly(boolean readOnly) 210 { 211 readOnlyOverride = readOnly; 212 } 214 218 public void setDirty(boolean d) 219 { 220 boolean editable = isEditable(); 221 222 if(d) 223 { 224 if(editable) 225 dirty = true; 226 } 227 else 228 { 229 dirty = false; 230 231 if(!isUndoInProgress()) 234 { 235 undoMgr.bufferSaved(); 238 } 239 } 240 } 242 244 246 251 public void readLock() 252 { 253 lock.readLock(); 254 } 256 261 public void readUnlock() 262 { 263 lock.readUnlock(); 264 } 266 271 public void writeLock() 272 { 273 lock.writeLock(); 274 } 276 281 public void writeUnlock() 282 { 283 lock.writeUnlock(); 284 } 286 288 290 294 public int getLength() 295 { 296 return contentMgr.getLength(); 298 } 300 306 public int getLineCount() 307 { 308 return lineMgr.getLineCount(); 310 } 312 319 public int getLineOfOffset(int offset) 320 { 321 try 322 { 323 readLock(); 324 325 if(offset < 0 || offset > getLength()) 326 throw new ArrayIndexOutOfBoundsException (offset); 327 328 return lineMgr.getLineOfOffset(offset); 329 } 330 finally 331 { 332 readUnlock(); 333 } 334 } 336 344 public int getLineStartOffset(int line) 345 { 346 try 347 { 348 readLock(); 349 350 if(line < 0 || line >= lineMgr.getLineCount()) 351 throw new ArrayIndexOutOfBoundsException (line); 352 else if(line == 0) 353 return 0; 354 355 return lineMgr.getLineEndOffset(line - 1); 356 } 357 finally 358 { 359 readUnlock(); 360 } 361 } 363 372 public int getLineEndOffset(int line) 373 { 374 try 375 { 376 readLock(); 377 378 if(line < 0 || line >= lineMgr.getLineCount()) 379 throw new ArrayIndexOutOfBoundsException (line); 380 381 return lineMgr.getLineEndOffset(line); 382 } 383 finally 384 { 385 readUnlock(); 386 } 387 } 389 396 public int getLineLength(int line) 397 { 398 try 399 { 400 readLock(); 401 402 return getLineEndOffset(line) 403 - getLineStartOffset(line) - 1; 404 } 405 finally 406 { 407 readUnlock(); 408 } 409 } 411 415 public int getPriorNonEmptyLine(int lineIndex) 416 { 417 int returnValue = -1; 418 419 for(int i = lineIndex - 1; i >= 0; i--) 420 { 421 getLineText(i,seg); 422 if(seg.count != 0) 423 returnValue = i; 424 for(int j = 0; j < seg.count; j++) 425 { 426 char ch = seg.array[seg.offset + j]; 427 if(!Character.isWhitespace(ch)) 428 return i; 429 } 430 } 431 432 return returnValue; 435 } 437 439 441 449 public String getLineText(int line) 450 { 451 if(line < 0 || line >= lineMgr.getLineCount()) 452 throw new ArrayIndexOutOfBoundsException (line); 453 454 try 455 { 456 readLock(); 457 458 int start = line == 0 ? 0 : lineMgr.getLineEndOffset(line - 1); 459 int end = lineMgr.getLineEndOffset(line); 460 461 return getText(start,end - start - 1); 462 } 463 finally 464 { 465 readUnlock(); 466 } 467 } 469 482 public void getLineText(int line, Segment segment) 483 { 484 if(line < 0 || line >= lineMgr.getLineCount()) 485 throw new ArrayIndexOutOfBoundsException (line); 486 487 try 488 { 489 readLock(); 490 491 int start = line == 0 ? 0 : lineMgr.getLineEndOffset(line - 1); 492 int end = lineMgr.getLineEndOffset(line); 493 494 getText(start,end - start - 1,segment); 495 } 496 finally 497 { 498 readUnlock(); 499 } 500 } 502 508 public String getText(int start, int length) 509 { 510 try 511 { 512 readLock(); 513 514 if(start < 0 || length < 0 515 || start + length > contentMgr.getLength()) 516 throw new ArrayIndexOutOfBoundsException (start + ":" + length); 517 518 return contentMgr.getText(start,length); 519 } 520 finally 521 { 522 readUnlock(); 523 } 524 } 526 540 public void getText(int start, int length, Segment seg) 541 { 542 try 543 { 544 readLock(); 545 546 if(start < 0 || length < 0 547 || start + length > contentMgr.getLength()) 548 throw new ArrayIndexOutOfBoundsException (start + ":" + length); 549 550 contentMgr.getText(start,length,seg); 551 } 552 finally 553 { 554 readUnlock(); 555 } 556 } 558 565 public void insert(int offset, String str) 566 { 567 if(str == null) 568 return; 569 570 int len = str.length(); 571 572 if(len == 0) 573 return; 574 575 if(isReadOnly()) 576 throw new RuntimeException ("buffer read-only"); 577 578 try 579 { 580 writeLock(); 581 582 if(offset < 0 || offset > contentMgr.getLength()) 583 throw new ArrayIndexOutOfBoundsException (offset); 584 585 contentMgr.insert(offset,str); 586 587 integerArray.clear(); 588 589 for(int i = 0; i < len; i++) 590 { 591 if(str.charAt(i) == '\n') 592 integerArray.add(i + 1); 593 } 594 595 if(!undoInProgress) 596 { 597 undoMgr.contentInserted(offset,len,str,!dirty); 598 } 599 600 contentInserted(offset,len,integerArray); 601 } 602 finally 603 { 604 writeUnlock(); 605 } 606 } 608 615 public void insert(int offset, Segment seg) 616 { 617 if(seg.count == 0) 618 return; 619 620 if(isReadOnly()) 621 throw new RuntimeException ("buffer read-only"); 622 623 try 624 { 625 writeLock(); 626 627 if(offset < 0 || offset > contentMgr.getLength()) 628 throw new ArrayIndexOutOfBoundsException (offset); 629 630 contentMgr.insert(offset,seg); 631 632 integerArray.clear(); 633 634 for(int i = 0; i < seg.count; i++) 635 { 636 if(seg.array[seg.offset + i] == '\n') 637 integerArray.add(i + 1); 638 } 639 640 if(!undoInProgress) 641 { 642 undoMgr.contentInserted(offset,seg.count, 643 seg.toString(),!dirty); 644 } 645 646 contentInserted(offset,seg.count,integerArray); 647 } 648 finally 649 { 650 writeUnlock(); 651 } 652 } 654 660 public void remove(int offset, int length) 661 { 662 if(length == 0) 663 return; 664 665 if(isReadOnly()) 666 throw new RuntimeException ("buffer read-only"); 667 668 try 669 { 670 transaction = true; 671 672 writeLock(); 673 674 if(offset < 0 || length < 0 675 || offset + length > contentMgr.getLength()) 676 throw new ArrayIndexOutOfBoundsException (offset + ":" + length); 677 678 int startLine = lineMgr.getLineOfOffset(offset); 679 int endLine = lineMgr.getLineOfOffset(offset + length); 680 681 int numLines = endLine - startLine; 682 683 if(!undoInProgress && !loading) 684 { 685 undoMgr.contentRemoved(offset,length, 686 getText(offset,length), 687 !dirty); 688 } 689 690 firePreContentRemoved(startLine,offset,numLines,length); 691 692 contentMgr.remove(offset,length); 693 lineMgr.contentRemoved(startLine,offset,numLines,length); 694 positionMgr.contentRemoved(offset,length); 695 696 fireContentRemoved(startLine,offset,numLines,length); 697 698 699 if(!undoInProgress && !insideCompoundEdit()) 700 fireTransactionComplete(); 701 702 setDirty(true); 703 } 704 finally 705 { 706 transaction = false; 707 708 writeUnlock(); 709 } 710 } 712 714 716 722 public void removeTrailingWhiteSpace(int[] lines) 723 { 724 try 725 { 726 beginCompoundEdit(); 727 728 for(int i = 0; i < lines.length; i++) 729 { 730 int pos, lineStart, lineEnd, tail; 731 732 getLineText(lines[i],seg); 733 734 if (seg.count == 0) continue; 736 737 lineStart = seg.offset; 738 lineEnd = seg.offset + seg.count - 1; 739 740 for (pos = lineEnd; pos >= lineStart; pos--) 741 { 742 if (!Character.isWhitespace(seg.array[pos])) 743 break; 744 } 745 746 tail = lineEnd - pos; 747 748 if (tail == 0) continue; 750 751 remove(getLineEndOffset(lines[i]) - 1 - tail,tail); 752 } 753 } 754 finally 755 { 756 endCompoundEdit(); 757 } 758 } 760 766 public void shiftIndentLeft(int[] lines) 767 { 768 int tabSize = getTabSize(); 769 int indentSize = getIndentSize(); 770 boolean noTabs = getBooleanProperty("noTabs"); 771 772 try 773 { 774 beginCompoundEdit(); 775 776 for(int i = 0; i < lines.length; i++) 777 { 778 int lineStart = getLineStartOffset(lines[i]); 779 String line = getLineText(lines[i]); 780 int whiteSpace = StandardUtilities 781 .getLeadingWhiteSpace(line); 782 if(whiteSpace == 0) 783 continue; 784 int whiteSpaceWidth = Math.max(0,StandardUtilities 785 .getLeadingWhiteSpaceWidth(line,tabSize) 786 - indentSize); 787 788 insert(lineStart + whiteSpace,StandardUtilities 789 .createWhiteSpace(whiteSpaceWidth, 790 noTabs ? 0 : tabSize)); 791 remove(lineStart,whiteSpace); 792 } 793 794 } 795 finally 796 { 797 endCompoundEdit(); 798 } 799 } 801 807 public void shiftIndentRight(int[] lines) 808 { 809 try 810 { 811 beginCompoundEdit(); 812 813 int tabSize = getTabSize(); 814 int indentSize = getIndentSize(); 815 boolean noTabs = getBooleanProperty("noTabs"); 816 for(int i = 0; i < lines.length; i++) 817 { 818 int lineStart = getLineStartOffset(lines[i]); 819 String line = getLineText(lines[i]); 820 int whiteSpace = StandardUtilities 821 .getLeadingWhiteSpace(line); 822 823 827 int whiteSpaceWidth = StandardUtilities 828 .getLeadingWhiteSpaceWidth( 829 line,tabSize) + indentSize; 830 insert(lineStart + whiteSpace,StandardUtilities 831 .createWhiteSpace(whiteSpaceWidth, 832 noTabs ? 0 : tabSize)); 833 remove(lineStart,whiteSpace); 834 } 835 } 836 finally 837 { 838 endCompoundEdit(); 839 } 840 } 842 849 public void indentLines(int start, int end) 850 { 851 try 852 { 853 beginCompoundEdit(); 854 for(int i = start; i <= end; i++) 855 indentLine(i,true); 856 } 857 finally 858 { 859 endCompoundEdit(); 860 } 861 } 863 869 public void indentLines(int[] lines) 870 { 871 try 872 { 873 beginCompoundEdit(); 874 for(int i = 0; i < lines.length; i++) 875 indentLine(lines[i],true); 876 } 877 finally 878 { 879 endCompoundEdit(); 880 } 881 } 883 887 public boolean indentLine(int lineIndex, boolean canIncreaseIndent, 888 boolean canDecreaseIndent) 889 { 890 return indentLine(lineIndex,canDecreaseIndent); 891 } 893 902 public boolean indentLine(int lineIndex, boolean canDecreaseIndent) 903 { 904 int[] whitespaceChars = new int[1]; 905 int currentIndent = getCurrentIndentForLine(lineIndex, 906 whitespaceChars); 907 int idealIndent = getIdealIndentForLine(lineIndex); 908 909 if(idealIndent == -1 || idealIndent == currentIndent 910 || (!canDecreaseIndent && idealIndent < currentIndent)) 911 return false; 912 913 try 915 { 916 beginCompoundEdit(); 917 918 int start = getLineStartOffset(lineIndex); 919 920 remove(start,whitespaceChars[0]); 921 insert(start,StandardUtilities.createWhiteSpace( 922 idealIndent, getBooleanProperty("noTabs") ? 0 : getTabSize())); 923 } 924 finally 925 { 926 endCompoundEdit(); 927 } 928 929 return true; 930 } 932 940 public int getCurrentIndentForLine(int lineIndex, int[] whitespaceChars) 941 { 942 getLineText(lineIndex,seg); 943 944 int tabSize = getTabSize(); 945 946 int currentIndent = 0; 947 loop: for(int i = 0; i < seg.count; i++) 948 { 949 char c = seg.array[seg.offset + i]; 950 switch(c) 951 { 952 case ' ': 953 currentIndent++; 954 if(whitespaceChars != null) 955 whitespaceChars[0]++; 956 break; 957 case '\t': 958 currentIndent += tabSize - (currentIndent 959 % tabSize); 960 if(whitespaceChars != null) 961 whitespaceChars[0]++; 962 break; 963 default: 964 break loop; 965 } 966 } 967 968 return currentIndent; 969 } 971 977 public int getIdealIndentForLine(int lineIndex) 978 { 979 int prevLineIndex = getPriorNonEmptyLine(lineIndex); 980 int prevPrevLineIndex = prevLineIndex < 0 ? -1 981 : getPriorNonEmptyLine(prevLineIndex); 982 983 int oldIndent = prevLineIndex == -1 ? 0 : 984 StandardUtilities.getLeadingWhiteSpaceWidth( 985 getLineText(prevLineIndex), 986 getTabSize()); 987 int newIndent = oldIndent; 988 989 List<IndentRule> indentRules = getIndentRules(lineIndex); 990 List<IndentAction> actions = new LinkedList<IndentAction>(); 991 for (int i = 0;i<indentRules.size();i++) 992 { 993 IndentRule rule = indentRules.get(i); 994 rule.apply(this,lineIndex,prevLineIndex, 995 prevPrevLineIndex,actions); 996 } 997 998 999 for (IndentAction action : actions) 1000 { 1001 newIndent = action.calculateIndent(this, lineIndex, 1002 oldIndent, newIndent); 1003 if (newIndent < 0) 1004 newIndent = 0; 1005 1006 if (!action.keepChecking()) 1007 break; 1008 } 1009 1010 return newIndent; 1011 } 1013 1022 public int getVirtualWidth(int line, int column) 1023 { 1024 try 1025 { 1026 readLock(); 1027 1028 int start = getLineStartOffset(line); 1029 getText(start,column,seg); 1030 1031 return StandardUtilities.getVirtualWidth(seg,getTabSize()); 1032 } 1033 finally 1034 { 1035 readUnlock(); 1036 } 1037 } 1039 1054 public int getOffsetOfVirtualColumn(int line, int column, 1055 int[] totalVirtualWidth) 1056 { 1057 try 1058 { 1059 readLock(); 1060 1061 getLineText(line,seg); 1062 1063 return StandardUtilities.getOffsetOfVirtualColumn(seg, 1064 getTabSize(),column,totalVirtualWidth); 1065 } 1066 finally 1067 { 1068 readUnlock(); 1069 } 1070 } 1072 1081 public void insertAtColumn(int line, int col, String str) 1082 { 1083 try 1084 { 1085 writeLock(); 1086 1087 int[] total = new int[1]; 1088 int offset = getOffsetOfVirtualColumn(line,col,total); 1089 if(offset == -1) 1090 { 1091 offset = getLineEndOffset(line) - 1; 1092 str = StandardUtilities.createWhiteSpace(col - total[0],0) + str; 1093 } 1094 else 1095 offset += getLineStartOffset(line); 1096 1097 insert(offset,str); 1098 } 1099 finally 1100 { 1101 writeUnlock(); 1102 } 1103 } 1105 1118 public int insertIndented(int offset, String text) 1119 { 1120 try 1121 { 1122 beginCompoundEdit(); 1123 1124 int firstLine = getLineOfOffset(offset); 1126 String lineText = getLineText(firstLine); 1127 int leadingIndent 1128 = StandardUtilities.getLeadingWhiteSpaceWidth( 1129 lineText,getTabSize()); 1130 1131 String whiteSpace = StandardUtilities.createWhiteSpace( 1132 leadingIndent,getBooleanProperty("noTabs") 1133 ? 0 : getTabSize()); 1134 1135 insert(offset,text); 1136 1137 int lastLine = getLineOfOffset(offset + text.length()); 1138 1139 for(int i = firstLine + 1; i <= lastLine; i++) 1142 { 1143 insert(getLineStartOffset(i),whiteSpace); 1144 } 1145 1146 return whiteSpace.length(); 1147 } 1148 finally 1149 { 1150 endCompoundEdit(); 1151 } 1152 } 1154 1161 public boolean isElectricKey(char ch) 1162 { 1163 Mode mode = jEdit.getMode(tokenMarker.getMainRuleSet().getModeName()); 1164 return mode.isElectricKey(ch); 1165 } 1167 1173 public boolean isElectricKey(char ch, int line) 1174 { 1175 TokenMarker.LineContext ctx = lineMgr.getLineContext(line); 1176 Mode mode = jEdit.getMode(ctx.rules.getModeName()); 1177 1178 if( mode == null ) 1180 return false; 1181 return mode.isElectricKey(ch); 1182 } 1184 1186 1188 1196 public void markTokens(int lineIndex, TokenHandler tokenHandler) 1197 { 1198 Segment seg; 1199 if(SwingUtilities.isEventDispatchThread()) 1200 seg = this.seg; 1201 else 1202 seg = new Segment(); 1203 1204 if(lineIndex < 0 || lineIndex >= lineMgr.getLineCount()) 1205 throw new ArrayIndexOutOfBoundsException (lineIndex); 1206 1207 int firstInvalidLineContext = lineMgr.getFirstInvalidLineContext(); 1208 int start; 1209 if(textMode || firstInvalidLineContext == -1) 1210 { 1211 start = lineIndex; 1212 } 1213 else 1214 { 1215 start = Math.min(firstInvalidLineContext, 1216 lineIndex); 1217 } 1218 1219 if(Debug.TOKEN_MARKER_DEBUG) 1220 Log.log(Log.DEBUG,this,"tokenize from " + start + " to " + lineIndex); 1221 TokenMarker.LineContext oldContext = null; 1222 TokenMarker.LineContext context = null; 1223 for(int i = start; i <= lineIndex; i++) 1224 { 1225 getLineText(i,seg); 1226 1227 oldContext = lineMgr.getLineContext(i); 1228 1229 TokenMarker.LineContext prevContext = ( 1230 (i == 0 || textMode) ? null 1231 : lineMgr.getLineContext(i - 1) 1232 ); 1233 1234 context = tokenMarker.markTokens(prevContext, 1235 (i == lineIndex ? tokenHandler 1236 : DummyTokenHandler.INSTANCE),seg); 1237 lineMgr.setLineContext(i,context); 1238 } 1239 1240 int lineCount = lineMgr.getLineCount(); 1241 if(lineCount - 1 == lineIndex) 1242 lineMgr.setFirstInvalidLineContext(-1); 1243 else if(oldContext != context) 1244 lineMgr.setFirstInvalidLineContext(lineIndex + 1); 1245 else if(firstInvalidLineContext == -1) 1246 ; 1247 else 1248 { 1249 lineMgr.setFirstInvalidLineContext(Math.max( 1250 firstInvalidLineContext,lineIndex + 1)); 1251 } 1252 } 1254 public TokenMarker getTokenMarker() 1256 { 1257 return tokenMarker; 1258 } 1260 public void setTokenMarker(TokenMarker tokenMarker) 1262 { 1263 TokenMarker oldTokenMarker = this.tokenMarker; 1264 1265 this.tokenMarker = tokenMarker; 1266 1267 if(oldTokenMarker != null && tokenMarker != oldTokenMarker) 1269 { 1270 lineMgr.setFirstInvalidLineContext(0); 1271 } 1272 } 1274 1279 public Position createPosition(int offset) 1280 { 1281 try 1282 { 1283 readLock(); 1284 1285 if(offset < 0 || offset > contentMgr.getLength()) 1286 throw new ArrayIndexOutOfBoundsException (offset); 1287 1288 return positionMgr.createPosition(offset); 1289 } 1290 finally 1291 { 1292 readUnlock(); 1293 } 1294 } 1296 1298 1300 1306 public int getTabSize() 1307 { 1308 int tabSize = getIntegerProperty("tabSize",8); 1309 if(tabSize <= 0) 1310 return 8; 1311 else 1312 return tabSize; 1313 } 1315 1322 public int getIndentSize() 1323 { 1324 int indentSize = getIntegerProperty("indentSize",8); 1325 if(indentSize <= 0) 1326 return 8; 1327 else 1328 return indentSize; 1329 } 1331 1352 public Object getProperty(Object name) 1353 { 1354 synchronized(propertyLock) 1355 { 1356 PropValue o = properties.get(name); 1358 if(o != null) 1359 return o.value; 1360 1361 if(!(name instanceof String )) 1363 return null; 1364 1365 Object retVal = getDefaultProperty((String )name); 1366 1367 if(retVal == null) 1368 return null; 1369 else 1370 { 1371 properties.put(name,new PropValue(retVal,true)); 1372 return retVal; 1373 } 1374 } 1375 } 1377 public Object getDefaultProperty(String key) 1379 { 1380 return null; 1381 } 1383 1390 public void setProperty(String name, Object value) 1391 { 1392 if(value == null) 1393 properties.remove(name); 1394 else 1395 { 1396 PropValue test = properties.get(name); 1397 if(test == null) 1398 properties.put(name,new PropValue(value,false)); 1399 else if(test.value.equals(value)) 1400 { 1401 } 1403 else 1404 { 1405 test.value = value; 1406 test.defaultValue = false; 1407 } 1408 } 1409 } 1411 public void setDefaultProperty(String name, Object value) 1413 { 1414 properties.put(name,new PropValue(value,true)); 1415 } 1417 1423 public void unsetProperty(String name) 1424 { 1425 properties.remove(name); 1426 } 1428 public void resetCachedProperties() 1430 { 1431 Iterator<PropValue> iter = properties.values().iterator(); 1434 while(iter.hasNext()) 1435 { 1436 PropValue value = iter.next(); 1437 if(value.defaultValue) 1438 iter.remove(); 1439 } 1440 } 1442 1448 public String getStringProperty(String name) 1449 { 1450 Object obj = getProperty(name); 1451 if(obj != null) 1452 return obj.toString(); 1453 else 1454 return null; 1455 } 1457 1464 public void setStringProperty(String name, String value) 1465 { 1466 setProperty(name,value); 1467 } 1469 1475 public boolean getBooleanProperty(String name) 1476 { 1477 Object obj = getProperty(name); 1478 if(obj instanceof Boolean ) 1479 return (Boolean )obj; 1480 else if("true".equals(obj) || "on".equals(obj) || "yes".equals(obj)) 1481 return true; 1482 else 1483 return false; 1484 } 1486 1493 public void setBooleanProperty(String name, boolean value) 1494 { 1495 setProperty(name,value ? Boolean.TRUE : Boolean.FALSE); 1496 } 1498 1504 public int getIntegerProperty(String name, int defaultValue) 1505 { 1506 boolean defaultValueFlag; 1507 Object obj; 1508 PropValue value = properties.get(name); 1509 if(value != null) 1510 { 1511 obj = value.value; 1512 defaultValueFlag = value.defaultValue; 1513 } 1514 else 1515 { 1516 obj = getProperty(name); 1517 defaultValueFlag = true; 1519 } 1520 1521 if(obj == null) 1522 return defaultValue; 1523 else if(obj instanceof Number ) 1524 return ((Number )obj).intValue(); 1525 else 1526 { 1527 try 1528 { 1529 int returnValue = Integer.parseInt( 1530 obj.toString().trim()); 1531 properties.put(name,new PropValue( 1532 returnValue, 1533 defaultValueFlag)); 1534 return returnValue; 1535 } 1536 catch(Exception e) 1537 { 1538 return defaultValue; 1539 } 1540 } 1541 } 1543 1550 public void setIntegerProperty(String name, int value) 1551 { 1552 setProperty(name,value); 1553 } 1555 1563 public Pattern getPatternProperty(String name, int flags) { 1564 synchronized(propertyLock) 1565 { 1566 boolean defaultValueFlag; 1567 Object obj; 1568 PropValue value = properties.get(name); 1569 if(value != null) 1570 { 1571 obj = value.value; 1572 defaultValueFlag = value.defaultValue; 1573 } 1574 else 1575 { 1576 obj = getProperty(name); 1577 defaultValueFlag = true; 1579 } 1580 1581 if(obj == null) 1582 return null; 1583 else if (obj instanceof Pattern ) 1584 return (Pattern ) obj; 1585 else 1586 { 1587 Pattern re = Pattern.compile(obj.toString(),flags); 1588 properties.put(name,new PropValue(re, 1589 defaultValueFlag)); 1590 return re; 1591 } 1592 } 1593 } 1595 1600 public ParserRuleSet getRuleSetAtOffset(int offset) 1601 { 1602 int line = getLineOfOffset(offset); 1603 offset -= getLineStartOffset(line); 1604 if(offset != 0) 1605 offset--; 1606 1607 DefaultTokenHandler tokens = new DefaultTokenHandler(); 1608 markTokens(line,tokens); 1609 Token token = TextUtilities.getTokenAtOffset(tokens.getTokens(),offset); 1610 return token.rules; 1611 } 1613 1621 public KeywordMap getKeywordMapAtOffset(int offset) 1622 { 1623 return getRuleSetAtOffset(offset).getKeywords(); 1624 } 1626 1635 public String getContextSensitiveProperty(int offset, String name) 1636 { 1637 ParserRuleSet rules = getRuleSetAtOffset(offset); 1638 1639 Object value = null; 1640 1641 Map<String , String > rulesetProps = rules.getProperties(); 1642 if(rulesetProps != null) 1643 value = rulesetProps.get(name); 1644 1645 if(value == null) 1646 return null; 1647 else 1648 return String.valueOf(value); 1649 } 1651 1653 1655 1660 public boolean isFoldStart(int line) 1661 { 1662 return line != getLineCount() - 1 1663 && getFoldLevel(line) < getFoldLevel(line + 1); 1664 } 1666 1671 public boolean isFoldEnd(int line) 1672 { 1673 return line != getLineCount() - 1 1674 && getFoldLevel(line) > getFoldLevel(line + 1); 1675 } 1677 1682 public void invalidateCachedFoldLevels() 1683 { 1684 lineMgr.setFirstInvalidFoldLevel(0); 1685 fireFoldLevelChanged(0,getLineCount()); 1686 } 1688 1694 public int getFoldLevel(int line) 1695 { 1696 if(line < 0 || line >= lineMgr.getLineCount()) 1697 throw new ArrayIndexOutOfBoundsException (line); 1698 1699 if(foldHandler instanceof DummyFoldHandler) 1700 return 0; 1701 1702 int firstInvalidFoldLevel = lineMgr.getFirstInvalidFoldLevel(); 1703 if(firstInvalidFoldLevel == -1 || line < firstInvalidFoldLevel) 1704 { 1705 return lineMgr.getFoldLevel(line); 1706 } 1707 else 1708 { 1709 if(Debug.FOLD_DEBUG) 1710 Log.log(Log.DEBUG,this,"Invalid fold levels from " + firstInvalidFoldLevel + " to " + line); 1711 1712 int newFoldLevel = 0; 1713 boolean changed = false; 1714 1715 for(int i = firstInvalidFoldLevel; i <= line; i++) 1716 { 1717 newFoldLevel = foldHandler.getFoldLevel(this,i,seg); 1718 if(newFoldLevel != lineMgr.getFoldLevel(i)) 1719 { 1720 if(Debug.FOLD_DEBUG) 1721 Log.log(Log.DEBUG,this,i + " fold level changed"); 1722 changed = true; 1723 } 1724 lineMgr.setFoldLevel(i,newFoldLevel); 1725 } 1726 1727 if(line == lineMgr.getLineCount() - 1) 1728 lineMgr.setFirstInvalidFoldLevel(-1); 1729 else 1730 lineMgr.setFirstInvalidFoldLevel(line + 1); 1731 1732 if(changed) 1733 { 1734 if(Debug.FOLD_DEBUG) 1735 Log.log(Log.DEBUG,this,"fold level changed: " + firstInvalidFoldLevel + ',' + line); 1736 fireFoldLevelChanged(firstInvalidFoldLevel,line); 1737 } 1738 1739 return newFoldLevel; 1740 } 1741 } 1743 1751 public int[] getFoldAtLine(int line) 1752 { 1753 int start, end; 1754 1755 if(isFoldStart(line)) 1756 { 1757 start = line; 1758 int foldLevel = getFoldLevel(line); 1759 1760 line++; 1761 1762 while(getFoldLevel(line) > foldLevel) 1763 { 1764 line++; 1765 1766 if(line == getLineCount()) 1767 break; 1768 } 1769 1770 end = line - 1; 1771 } 1772 else 1773 { 1774 start = line; 1775 int foldLevel = getFoldLevel(line); 1776 while(getFoldLevel(start) >= foldLevel) 1777 { 1778 if(start == 0) 1779 break; 1780 else 1781 start--; 1782 } 1783 1784 end = line; 1785 while(getFoldLevel(end) >= foldLevel) 1786 { 1787 end++; 1788 1789 if(end == getLineCount()) 1790 break; 1791 } 1792 1793 end--; 1794 } 1795 1796 while(getLineLength(end) == 0 && end > start) 1797 end--; 1798 1799 return new int[] { start, end }; 1800 } 1802 1807 public FoldHandler getFoldHandler() 1808 { 1809 return foldHandler; 1810 } 1812 1817 public void setFoldHandler(FoldHandler foldHandler) 1818 { 1819 FoldHandler oldFoldHandler = this.foldHandler; 1820 1821 if(foldHandler.equals(oldFoldHandler)) 1822 return; 1823 1824 this.foldHandler = foldHandler; 1825 1826 lineMgr.setFirstInvalidFoldLevel(0); 1827 1828 fireFoldHandlerChanged(); 1829 } 1831 1833 1835 1841 public void undo(JEditTextArea textArea) 1842 { 1843 if(undoMgr == null) 1844 return; 1845 1846 if(!isEditable()) 1847 { 1848 textArea.getToolkit().beep(); 1849 return; 1850 } 1851 1852 try 1853 { 1854 writeLock(); 1855 1856 undoInProgress = true; 1857 int caret = undoMgr.undo(); 1858 1859 if(caret == -1) 1860 textArea.getToolkit().beep(); 1861 else 1862 textArea.setCaretPosition(caret); 1863 1864 fireTransactionComplete(); 1865 } 1866 finally 1867 { 1868 undoInProgress = false; 1869 1870 writeUnlock(); 1871 } 1872 } 1874 1880 public void redo(JEditTextArea textArea) 1881 { 1882 if(undoMgr == null) 1883 return; 1884 1885 if(!isEditable()) 1886 { 1887 Toolkit.getDefaultToolkit().beep(); 1888 return; 1889 } 1890 1891 try 1892 { 1893 writeLock(); 1894 1895 undoInProgress = true; 1896 int caret = undoMgr.redo(); 1897 if(caret == -1) 1898 textArea.getToolkit().beep(); 1899 else 1900 textArea.setCaretPosition(caret); 1901 1902 fireTransactionComplete(); 1903 } 1904 finally 1905 { 1906 undoInProgress = false; 1907 1908 writeUnlock(); 1909 } 1910 } 1912 1920 public boolean isTransactionInProgress() 1921 { 1922 return transaction || undoInProgress || insideCompoundEdit(); 1923 } 1925 1936 public void beginCompoundEdit() 1937 { 1938 try 1939 { 1940 writeLock(); 1941 1942 undoMgr.beginCompoundEdit(); 1943 } 1944 finally 1945 { 1946 writeUnlock(); 1947 } 1948 } 1950 1957 public void endCompoundEdit() 1958 { 1959 try 1960 { 1961 writeLock(); 1962 1963 undoMgr.endCompoundEdit(); 1964 1965 if(!insideCompoundEdit()) 1966 fireTransactionComplete(); 1967 } 1968 finally 1969 { 1970 writeUnlock(); 1971 } 1972 } 1974 1979 public boolean insideCompoundEdit() 1980 { 1981 return undoMgr.insideCompoundEdit(); 1982 } 1984 1989 public boolean isUndoInProgress() 1990 { 1991 return undoInProgress; 1992 } 1994 1996 public static final int NORMAL_PRIORITY = 0; 1998 public static final int HIGH_PRIORITY = 1; 1999 2000 static class Listener 2001 { 2002 BufferListener listener; 2003 int priority; 2004 2005 Listener(BufferListener listener, int priority) 2006 { 2007 this.listener = listener; 2008 this.priority = priority; 2009 } 2010 } 2011 2012 2020 public void addBufferListener(BufferListener listener, 2021 int priority) 2022 { 2023 Listener l = new Listener(listener,priority); 2024 for(int i = 0; i < bufferListeners.size(); i++) 2025 { 2026 Listener _l = bufferListeners.get(i); 2027 if(_l.priority < priority) 2028 { 2029 bufferListeners.add(i,l); 2030 return; 2031 } 2032 } 2033 bufferListeners.add(l); 2034 } 2036 2042 public void addBufferListener(BufferListener listener) 2043 { 2044 addBufferListener(listener,NORMAL_PRIORITY); 2045 } 2047 2053 public void removeBufferListener(BufferListener listener) 2054 { 2055 for(int i = 0; i < bufferListeners.size(); i++) 2056 { 2057 if(bufferListeners.get(i).listener == listener) 2058 { 2059 bufferListeners.remove(i); 2060 return; 2061 } 2062 } 2063 } 2065 2070 public BufferListener[] getBufferListeners() 2071 { 2072 BufferListener[] returnValue 2073 = new BufferListener[ 2074 bufferListeners.size()]; 2075 for(int i = 0; i < returnValue.length; i++) 2076 { 2077 returnValue[i] = bufferListeners.get(i).listener; 2078 } 2079 return returnValue; 2080 } 2082 2084 2086 protected Segment seg; 2087 protected boolean textMode; 2088 protected UndoManager undoMgr; 2089 2090 2092 protected void fireFoldLevelChanged(int start, int end) 2094 { 2095 for(int i = 0; i < bufferListeners.size(); i++) 2096 { 2097 try 2098 { 2099 getListener(i).foldLevelChanged(this,start,end); 2100 } 2101 catch(Throwable t) 2102 { 2103 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2104 Log.log(Log.ERROR,this,t); 2105 } 2106 } 2107 } 2109 protected void fireContentInserted(int startLine, int offset, 2111 int numLines, int length) 2112 { 2113 for(int i = 0; i < bufferListeners.size(); i++) 2114 { 2115 try 2116 { 2117 getListener(i).contentInserted(this,startLine, 2118 offset,numLines,length); 2119 } 2120 catch(Throwable t) 2121 { 2122 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2123 Log.log(Log.ERROR,this,t); 2124 } 2125 } 2126 } 2128 protected void fireContentRemoved(int startLine, int offset, 2130 int numLines, int length) 2131 { 2132 for(int i = 0; i < bufferListeners.size(); i++) 2133 { 2134 try 2135 { 2136 getListener(i).contentRemoved(this,startLine, 2137 offset,numLines,length); 2138 } 2139 catch(Throwable t) 2140 { 2141 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2142 Log.log(Log.ERROR,this,t); 2143 } 2144 } 2145 } 2147 protected void firePreContentRemoved(int startLine, int offset, 2149 int numLines, int length) 2150 { 2151 for(int i = 0; i < bufferListeners.size(); i++) 2152 { 2153 try 2154 { 2155 getListener(i).preContentRemoved(this,startLine, 2156 offset,numLines,length); 2157 } 2158 catch(Throwable t) 2159 { 2160 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2161 Log.log(Log.ERROR,this,t); 2162 } 2163 } 2164 } 2166 protected void fireTransactionComplete() 2168 { 2169 for(int i = 0; i < bufferListeners.size(); i++) 2170 { 2171 try 2172 { 2173 getListener(i).transactionComplete(this); 2174 } 2175 catch(Throwable t) 2176 { 2177 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2178 Log.log(Log.ERROR,this,t); 2179 } 2180 } 2181 } 2183 protected void fireFoldHandlerChanged() 2185 { 2186 for(int i = 0; i < bufferListeners.size(); i++) 2187 { 2188 try 2189 { 2190 getListener(i).foldHandlerChanged(this); 2191 } 2192 catch(Throwable t) 2193 { 2194 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2195 Log.log(Log.ERROR,this,t); 2196 } 2197 } 2198 } 2200 protected void fireBufferLoaded() 2202 { 2203 for(int i = 0; i < bufferListeners.size(); i++) 2204 { 2205 try 2206 { 2207 getListener(i).bufferLoaded(this); 2208 } 2209 catch(Throwable t) 2210 { 2211 Log.log(Log.ERROR,this,"Exception while sending buffer event to "+getListener(i)+" :"); 2212 Log.log(Log.ERROR,this,t); 2213 } 2214 } 2215 } 2217 2219 protected boolean isFileReadOnly() 2221 { 2222 return readOnly; 2223 } 2225 protected void setFileReadOnly(boolean readOnly) 2227 { 2228 this.readOnly = readOnly; 2229 } 2231 protected void loadText(Segment seg, IntegerArray endOffsets) 2233 { 2234 if(seg == null) 2235 seg = new Segment(new char[1024],0,0); 2236 2237 if(endOffsets == null) 2238 { 2239 endOffsets = new IntegerArray(); 2240 endOffsets.add(1); 2241 } 2242 2243 try 2244 { 2245 writeLock(); 2246 2247 int length = getLength(); 2250 2251 firePreContentRemoved(0,0,getLineCount() 2252 - 1,length); 2253 2254 contentMgr.remove(0,length); 2255 lineMgr.contentRemoved(0,0,getLineCount() 2256 - 1,length); 2257 positionMgr.contentRemoved(0,length); 2258 fireContentRemoved(0,0,getLineCount() 2259 - 1,length); 2260 2261 contentMgr._setContent(seg.array,seg.count); 2265 2266 lineMgr._contentInserted(endOffsets); 2267 positionMgr.contentInserted(0,seg.count); 2268 2269 fireContentInserted(0,0, 2270 endOffsets.getSize() - 1, 2271 seg.count - 1); 2272 } 2273 finally 2274 { 2275 writeUnlock(); 2276 } 2277 } 2279 protected void invalidateFoldLevels() 2281 { 2282 lineMgr.setFirstInvalidFoldLevel(0); 2283 } 2285 protected void parseBufferLocalProperties() 2287 { 2288 int lastLine = Math.min(9,getLineCount() - 1); 2289 parseBufferLocalProperties(getText(0,getLineEndOffset(lastLine) - 1)); 2290 2291 int firstLine = Math.max(lastLine + 1, getLineCount() - 10); 2294 if(firstLine < getLineCount()) 2295 { 2296 int length = getLineEndOffset(getLineCount() - 1) 2297 - (getLineStartOffset(firstLine) + 1); 2298 parseBufferLocalProperties(getText(getLineStartOffset(firstLine),length)); 2299 } 2300 } 2302 protected static class PropValue 2304 { 2305 PropValue(Object value, boolean defaultValue) 2306 { 2307 if(value == null) 2308 throw new NullPointerException (); 2309 this.value = value; 2310 this.defaultValue = defaultValue; 2311 } 2312 2313 Object value; 2314 2315 2320 boolean defaultValue; 2321 2322 2325 public String toString() 2326 { 2327 return value.toString(); 2328 } 2329 } 2331 2333 private List<Listener> bufferListeners; 2335 private ReadWriteLock lock; 2336 private ContentManager contentMgr; 2337 private LineManager lineMgr; 2338 private PositionManager positionMgr; 2339 private FoldHandler foldHandler; 2340 private IntegerArray integerArray; 2341 private TokenMarker tokenMarker; 2342 private boolean undoInProgress; 2343 private boolean dirty; 2344 private boolean readOnly; 2345 private boolean readOnlyOverride; 2346 private boolean transaction; 2347 private boolean loading; 2348 private boolean io; 2349 private final Map<Object , PropValue> properties; 2350 private final Object propertyLock; 2351 2352 private BufferListener getListener(int index) 2354 { 2355 return bufferListeners.get(index).listener; 2356 } 2358 private void contentInserted(int offset, int length, 2360 IntegerArray endOffsets) 2361 { 2362 try 2363 { 2364 transaction = true; 2365 2366 int startLine = lineMgr.getLineOfOffset(offset); 2367 int numLines = endOffsets.getSize(); 2368 2369 lineMgr.contentInserted(startLine,offset,numLines,length, 2370 endOffsets); 2371 positionMgr.contentInserted(offset,length); 2372 2373 setDirty(true); 2374 2375 if(!loading) 2376 { 2377 fireContentInserted(startLine,offset,numLines,length); 2378 2379 if(!undoInProgress && !insideCompoundEdit()) 2380 fireTransactionComplete(); 2381 } 2382 2383 } 2384 finally 2385 { 2386 transaction = false; 2387 } 2388 } 2390 private void parseBufferLocalProperties(String prop) 2392 { 2393 StringBuilder buf = new StringBuilder (); 2394 String name = null; 2395 boolean escape = false; 2396 for(int i = 0; i < prop.length(); i++) 2397 { 2398 char c = prop.charAt(i); 2399 switch(c) 2400 { 2401 case ':': 2402 if(escape) 2403 { 2404 escape = false; 2405 buf.append(':'); 2406 break; 2407 } 2408 if(name != null) 2409 { 2410 properties.put(name,new PropValue(buf.toString(),false)); 2416 name = null; 2417 } 2418 buf.setLength(0); 2419 break; 2420 case '=': 2421 if(escape) 2422 { 2423 escape = false; 2424 buf.append('='); 2425 break; 2426 } 2427 name = buf.toString(); 2428 buf.setLength(0); 2429 break; 2430 case '\\': 2431 if(escape) 2432 buf.append('\\'); 2433 escape = !escape; 2434 break; 2435 case 'n': 2436 if(escape) 2437 { buf.append('\n'); 2438 escape = false; 2439 break; 2440 } 2441 case 'r': 2442 if(escape) 2443 { buf.append('\r'); 2444 escape = false; 2445 break; 2446 } 2447 case 't': 2448 if(escape) 2449 { 2450 buf.append('\t'); 2451 escape = false; 2452 break; 2453 } 2454 default: 2455 buf.append(c); 2456 break; 2457 } 2458 } 2459 } 2461 private List<IndentRule> getIndentRules(int line) 2463 { 2464 String modeName = null; 2465 TokenMarker.LineContext ctx = lineMgr.getLineContext(line); 2466 if (ctx != null && ctx.rules != null) 2467 modeName = ctx.rules.getModeName(); 2468 if (modeName == null) 2469 modeName = tokenMarker.getMainRuleSet().getModeName(); 2470 return jEdit.getMode(modeName).getIndentRules(); 2471 } 2473 } 2475 | Popular Tags |