PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre5/org/gjt/sp/jedit/buffer/OffsetManager.java

#
Java | 629 lines | 429 code | 77 blank | 123 comment | 89 complexity | 87a9c314bdd304d59d9b9cbd6ce24c7c MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * OffsetManager.java - Manages line info, line start offsets, positions
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit.buffer;
  23. //{{{ Imports
  24. import javax.swing.text.*;
  25. import org.gjt.sp.jedit.syntax.*;
  26. import org.gjt.sp.jedit.Buffer;
  27. import org.gjt.sp.util.IntegerArray;
  28. import org.gjt.sp.util.Log;
  29. //}}}
  30. /**
  31. * A class internal to jEdit's document model. You should not use it
  32. * directly. To improve performance, none of the methods in this class
  33. * check for out of bounds access, nor are they thread-safe. The
  34. * <code>Buffer</code> class, through which these methods must be
  35. * called through, implements such protection.
  36. *
  37. * @author Slava Pestov
  38. * @version $Id: OffsetManager.java 3998 2002-01-28 04:20:54Z spestov $
  39. * @since jEdit 4.0pre1
  40. */
  41. public class OffsetManager
  42. {
  43. //{{{ OffsetManager constructor
  44. public OffsetManager(Buffer buffer)
  45. {
  46. this.buffer = buffer;
  47. lineInfo = new long[1];
  48. // make first line visible by default
  49. lineInfo[0] = 1L | (0xffL << VISIBLE_SHIFT);
  50. lineContext = new TokenMarker.LineContext[1];
  51. lineCount = 1;
  52. positions = new PosBottomHalf[100];
  53. virtualLineCounts = new int[8];
  54. for(int i = 0; i < 8; i++)
  55. virtualLineCounts[i] = 1;
  56. } //}}}
  57. //{{{ getLineCount() method
  58. public final int getLineCount()
  59. {
  60. return lineCount;
  61. } //}}}
  62. //{{{ getVirtualLineCount() method
  63. public final int getVirtualLineCount(int index)
  64. {
  65. return virtualLineCounts[index];
  66. } //}}}
  67. //{{{ setVirtualLineCount() method
  68. public final void setVirtualLineCount(int index, int lineCount)
  69. {
  70. virtualLineCounts[index] = lineCount;
  71. } //}}}
  72. //{{{ getLineOfOffset() method
  73. public int getLineOfOffset(int offset)
  74. {
  75. int start = 0;
  76. int end = lineCount - 1;
  77. for(;;)
  78. {
  79. switch(end - start)
  80. {
  81. case 0:
  82. if(getLineEndOffset(start) <= offset)
  83. return start + 1;
  84. else
  85. return start;
  86. case 1:
  87. if(getLineEndOffset(start) <= offset)
  88. {
  89. if(getLineEndOffset(end) <= offset)
  90. return end + 1;
  91. else
  92. return end;
  93. }
  94. else
  95. return start;
  96. default:
  97. int pivot = (end + start) / 2;
  98. int value = getLineEndOffset(pivot);
  99. if(value == offset)
  100. return pivot + 1;
  101. else if(value < offset)
  102. start = pivot + 1;
  103. else
  104. end = pivot - 1;
  105. break;
  106. }
  107. }
  108. } //}}}
  109. //{{{ getLineEndOffset() method
  110. public final int getLineEndOffset(int line)
  111. {
  112. return (int)(lineInfo[line] & END_MASK);
  113. } //}}}
  114. //{{{ isFoldLevelValid() method
  115. public final boolean isFoldLevelValid(int line)
  116. {
  117. return (lineInfo[line] & FOLD_LEVEL_VALID_MASK) != 0;
  118. } //}}}
  119. //{{{ getFoldLevel() method
  120. public final int getFoldLevel(int line)
  121. {
  122. return (int)((lineInfo[line] & FOLD_LEVEL_MASK)
  123. >> FOLD_LEVEL_SHIFT);
  124. } //}}}
  125. //{{{ setFoldLevel() method
  126. // Also sets 'fold level valid' flag
  127. public final void setFoldLevel(int line, int level)
  128. {
  129. lineInfo[line] = ((lineInfo[line] & ~FOLD_LEVEL_MASK)
  130. | ((long)level << FOLD_LEVEL_SHIFT)
  131. | FOLD_LEVEL_VALID_MASK);
  132. } //}}}
  133. //{{{ isLineVisible() method
  134. public final boolean isLineVisible(int line, int index)
  135. {
  136. long mask = 1L << (index + VISIBLE_SHIFT);
  137. return (lineInfo[line] & mask) != 0;
  138. } //}}}
  139. //{{{ setLineVisible() method
  140. public final void setLineVisible(int line, int index, boolean visible)
  141. {
  142. long mask = 1L << (index + VISIBLE_SHIFT);
  143. if(visible)
  144. lineInfo[line] = (lineInfo[line] | mask);
  145. else
  146. lineInfo[line] = (lineInfo[line] & ~mask);
  147. } //}}}
  148. //{{{ isLineContextValid() method
  149. public final boolean isLineContextValid(int line)
  150. {
  151. return (lineInfo[line] & CONTEXT_VALID_MASK) != 0;
  152. } //}}}
  153. //{{{ getLineContext() method
  154. public final TokenMarker.LineContext getLineContext(int line)
  155. {
  156. return lineContext[line];
  157. } //}}}
  158. //{{{ setLineContext() method
  159. // Also sets 'context valid' to true
  160. public final void setLineContext(int line, TokenMarker.LineContext context)
  161. {
  162. lineContext[line] = context;
  163. lineInfo[line] |= CONTEXT_VALID_MASK;
  164. } //}}}
  165. //{{{ createPosition() method
  166. // note: Buffer.createPosition() grabs a read lock, so the buffer
  167. // will not change during this method. however, if two stops call
  168. // it, there can be contention issues unless this method is
  169. // synchronized.
  170. // I could make Buffer.createPosition() grab a write lock, but then
  171. // it would be necessary to implement grabbing write locks within
  172. // read locks, since HyperSearch for example does everything inside
  173. // a read lock.
  174. public synchronized Position createPosition(int offset)
  175. {
  176. PosBottomHalf bh = null;
  177. for(int i = 0; i < positionCount; i++)
  178. {
  179. PosBottomHalf _bh = positions[i];
  180. if(_bh.offset == offset)
  181. {
  182. bh = _bh;
  183. break;
  184. }
  185. else if(_bh.offset > offset)
  186. {
  187. bh = new PosBottomHalf(offset);
  188. growPositionArray();
  189. System.arraycopy(positions,i,positions,i+1,
  190. positionCount - i);
  191. positionCount++;
  192. positions[i] = bh;
  193. break;
  194. }
  195. }
  196. if(bh == null)
  197. {
  198. bh = new PosBottomHalf(offset);
  199. growPositionArray();
  200. positions[positionCount++] = bh;
  201. }
  202. return new PosTopHalf(bh);
  203. } //}}}
  204. //{{{ expandFolds() method
  205. /**
  206. * Like <code>FoldVisibilityManager.expandFolds()</code>, but does
  207. * it for all fold visibility managers viewing this buffer. Should
  208. * only be called after loading.
  209. */
  210. public void expandFolds(int foldLevel)
  211. {
  212. int newVirtualLineCount = 0;
  213. foldLevel = (foldLevel - 1) * buffer.getIndentSize() + 1;
  214. /* this ensures that the first line is always visible */
  215. boolean seenVisibleLine = false;
  216. for(int i = 0; i < getLineCount(); i++)
  217. {
  218. if(!seenVisibleLine || buffer.getFoldLevel(i) < foldLevel)
  219. {
  220. seenVisibleLine = true;
  221. // Since only called on load, it already has
  222. // the VISIBLE_MASK set
  223. //lineInfo[i] |= VISIBLE_MASK;
  224. newVirtualLineCount++;
  225. }
  226. else
  227. lineInfo[i] &= ~VISIBLE_MASK;
  228. }
  229. for(int i = 0; i < virtualLineCounts.length; i++)
  230. {
  231. virtualLineCounts[i] = newVirtualLineCount;
  232. }
  233. } //}}}
  234. //{{{ contentInserted() method
  235. public void contentInserted(int startLine, int offset,
  236. int numLines, int length, IntegerArray endOffsets)
  237. {
  238. int endLine = startLine + numLines;
  239. //{{{ Update line info and line context arrays
  240. if(numLines > 0)
  241. {
  242. lineCount += numLines;
  243. if(lineInfo.length <= lineCount)
  244. {
  245. long[] lineInfoN = new long[(lineCount + 1) * 2];
  246. System.arraycopy(lineInfo,0,lineInfoN,0,
  247. lineInfo.length);
  248. lineInfo = lineInfoN;
  249. TokenMarker.LineContext[] lineContextN
  250. = new TokenMarker.LineContext[(lineCount + 1) * 2];
  251. System.arraycopy(lineContext,0,lineContextN,0,
  252. lineContext.length);
  253. lineContext = lineContextN;
  254. }
  255. System.arraycopy(lineInfo,startLine,lineInfo,
  256. endLine,lineCount - endLine);
  257. System.arraycopy(lineContext,startLine,lineContext,
  258. endLine,lineCount - endLine);
  259. //{{{ Find fold start of this line
  260. int foldLevel = buffer.getFoldLevel(startLine);
  261. long visible = (0xffL << VISIBLE_SHIFT);
  262. if(startLine != 0)
  263. {
  264. for(int i = startLine; i > 0; i--)
  265. {
  266. if(/* buffer.isFoldStart(i - 1)
  267. && */ buffer.getFoldLevel(i) <= foldLevel)
  268. {
  269. visible = (lineInfo[i] & VISIBLE_MASK);
  270. break;
  271. }
  272. }
  273. } //}}}
  274. for(int i = 0; i < numLines; i++)
  275. {
  276. // need the line end offset to be in place
  277. // for following fold level calculations
  278. lineInfo[startLine + i] = ((offset + endOffsets.get(i) + 1)
  279. & ~(FOLD_LEVEL_VALID_MASK | CONTEXT_VALID_MASK)
  280. | visible);
  281. }
  282. //{{{ Unrolled
  283. if((visible & (1L << (VISIBLE_SHIFT + 0))) != 0)
  284. virtualLineCounts[0] += numLines;
  285. if((visible & (1L << (VISIBLE_SHIFT + 1))) != 0)
  286. virtualLineCounts[1] += numLines;
  287. if((visible & (1L << (VISIBLE_SHIFT + 2))) != 0)
  288. virtualLineCounts[2] += numLines;
  289. if((visible & (1L << (VISIBLE_SHIFT + 3))) != 0)
  290. virtualLineCounts[3] += numLines;
  291. if((visible & (1L << (VISIBLE_SHIFT + 4))) != 0)
  292. virtualLineCounts[4] += numLines;
  293. if((visible & (1L << (VISIBLE_SHIFT + 5))) != 0)
  294. virtualLineCounts[5] += numLines;
  295. if((visible & (1L << (VISIBLE_SHIFT + 6))) != 0)
  296. virtualLineCounts[6] += numLines;
  297. if((visible & (1L << (VISIBLE_SHIFT + 7))) != 0)
  298. virtualLineCounts[7] += numLines;
  299. //}}}
  300. } //}}}
  301. //{{{ Update remaining line start offsets
  302. for(int i = endLine; i < lineCount; i++)
  303. {
  304. setLineEndOffset(i,getLineEndOffset(i) + length);
  305. lineInfo[i] &= ~(FOLD_LEVEL_VALID_MASK
  306. | CONTEXT_VALID_MASK);
  307. } //}}}
  308. updatePositionsForInsert(offset,length);
  309. } //}}}
  310. //{{{ contentRemoved() method
  311. public void contentRemoved(int startLine, int offset,
  312. int numLines, int length)
  313. {
  314. //{{{ Update virtual line counts
  315. for(int i = 0; i < numLines; i++)
  316. {
  317. long info = lineInfo[startLine + i];
  318. // Unrolled for max efficency
  319. if((info & (1L << (VISIBLE_SHIFT + 0))) != 0)
  320. virtualLineCounts[0]--;
  321. if((info & (1L << (VISIBLE_SHIFT + 1))) != 0)
  322. virtualLineCounts[1]--;
  323. if((info & (1L << (VISIBLE_SHIFT + 2))) != 0)
  324. virtualLineCounts[2]--;
  325. if((info & (1L << (VISIBLE_SHIFT + 3))) != 0)
  326. virtualLineCounts[3]--;
  327. if((info & (1L << (VISIBLE_SHIFT + 4))) != 0)
  328. virtualLineCounts[4]--;
  329. if((info & (1L << (VISIBLE_SHIFT + 5))) != 0)
  330. virtualLineCounts[5]--;
  331. if((info & (1L << (VISIBLE_SHIFT + 6))) != 0)
  332. virtualLineCounts[6]--;
  333. if((info & (1L << (VISIBLE_SHIFT + 7))) != 0)
  334. virtualLineCounts[7]--;
  335. } //}}}
  336. //{{{ Update line info and line context arrays
  337. if(numLines > 0)
  338. {
  339. lineCount -= numLines;
  340. System.arraycopy(lineInfo,startLine + numLines,lineInfo,
  341. startLine,lineCount - startLine);
  342. System.arraycopy(lineContext,startLine + numLines,lineContext,
  343. startLine,lineCount - startLine);
  344. } //}}}
  345. //{{{ Update remaining line start offsets
  346. for(int i = startLine; i < lineCount; i++)
  347. {
  348. setLineEndOffset(i,getLineEndOffset(i) - length);
  349. lineInfo[i] &= ~(FOLD_LEVEL_VALID_MASK
  350. | CONTEXT_VALID_MASK);
  351. } //}}}
  352. updatePositionsForRemove(offset,length);
  353. } //}}}
  354. //{{{ linesChanged() method
  355. public void linesChanged(int startLine, int numLines)
  356. {
  357. for(int i = 0; i < numLines; i++)
  358. {
  359. lineInfo[startLine + i] &= ~(FOLD_LEVEL_VALID_MASK
  360. | CONTEXT_VALID_MASK);
  361. lineContext[startLine + i] = null;
  362. }
  363. } //}}}
  364. //{{{ Private members
  365. /* {{{ Format of entires in line info array:
  366. * 0-31: end offset
  367. * 32-47: fold level
  368. * 48-55: visibility bit flags
  369. * 56: fold level valid flag
  370. * 57: context valid flag
  371. * 58-63: reserved
  372. *
  373. * Having all the info packed into a long is not very OO and makes the
  374. * code somewhat more complicated, but it saves a lot of memory.
  375. *
  376. * The new document model has just 12 bytes of overhead per line.
  377. * LineContext instances are now internalized, so only a few should
  378. * actually be in the heap.
  379. *
  380. * In the old document model there were 5 objects per line, for a
  381. * total of about 100 bytes, plus a cached token list, which used
  382. * another 100 or so bytes.
  383. * }}}*/
  384. private static final long END_MASK = 0x00000000ffffffffL;
  385. private static final long FOLD_LEVEL_MASK = 0x0000ffff00000000L;
  386. private static final int FOLD_LEVEL_SHIFT = 32;
  387. private static final long VISIBLE_MASK = 0x00ff000000000000L;
  388. private static final int VISIBLE_SHIFT = 48;
  389. private static final long FOLD_LEVEL_VALID_MASK = (1L<<56);
  390. private static final long CONTEXT_VALID_MASK = (1L<<57);
  391. //{{{ Instance variables
  392. private Buffer buffer;
  393. private long[] lineInfo;
  394. private TokenMarker.LineContext[] lineContext;
  395. private int lineCount;
  396. private PosBottomHalf[] positions;
  397. private int positionCount;
  398. private int[] virtualLineCounts;
  399. //}}}
  400. //{{{ setLineEndOffset() method
  401. private final void setLineEndOffset(int line, int end)
  402. {
  403. lineInfo[line] = ((lineInfo[line] & ~END_MASK) | end);
  404. } //}}}
  405. //{{{ growPositionArray() method
  406. private void growPositionArray()
  407. {
  408. if(positions.length < positionCount + 1)
  409. {
  410. PosBottomHalf[] newPositions = new PosBottomHalf[
  411. (positionCount + 1) * 2];
  412. System.arraycopy(positions,0,newPositions,0,positionCount);
  413. positions = newPositions;
  414. }
  415. } //}}}
  416. //{{{ removePosition() method
  417. private synchronized void removePosition(PosBottomHalf bh)
  418. {
  419. int index = -1;
  420. for(int i = 0; i < positionCount; i++)
  421. {
  422. if(positions[i] == bh)
  423. {
  424. index = i;
  425. break;
  426. }
  427. }
  428. System.arraycopy(positions,index + 1,positions,index,
  429. positionCount - index - 1);
  430. positions[--positionCount] = null;
  431. } //}}}
  432. //{{{ updatePositionsForInsert() method
  433. private void updatePositionsForInsert(int offset, int length)
  434. {
  435. if(positionCount == 0)
  436. return;
  437. int start = getPositionAtOffset(offset);
  438. for(int i = start; i < positionCount; i++)
  439. {
  440. PosBottomHalf bh = positions[i];
  441. if(bh.offset < offset)
  442. Log.log(Log.ERROR,this,"Screwed up: " + bh.offset);
  443. else
  444. bh.offset += length;
  445. }
  446. } //}}}
  447. //{{{ updatePositionsForRemove() method
  448. private void updatePositionsForRemove(int offset, int length)
  449. {
  450. if(positionCount == 0)
  451. return;
  452. int start = getPositionAtOffset(offset);
  453. for(int i = start; i < positionCount; i++)
  454. {
  455. PosBottomHalf bh = positions[i];
  456. if(bh.offset < offset)
  457. Log.log(Log.ERROR,this,"Screwed up: " + bh.offset);
  458. else if(bh.offset < offset + length)
  459. bh.offset = offset;
  460. else
  461. bh.offset -= length;
  462. }
  463. } //}}}
  464. //{{{ getPositionAtOffset() method
  465. private int getPositionAtOffset(int offset)
  466. {
  467. int start = 0;
  468. int end = positionCount - 1;
  469. PosBottomHalf bh;
  470. loop: for(;;)
  471. {
  472. switch(end - start)
  473. {
  474. case 0:
  475. bh = positions[start];
  476. if(bh.offset < offset)
  477. start++;
  478. break loop;
  479. case 1:
  480. bh = positions[end];
  481. if(bh.offset < offset)
  482. {
  483. start = end + 1;
  484. }
  485. else
  486. {
  487. bh = positions[start];
  488. if(bh.offset < offset)
  489. {
  490. start++;
  491. }
  492. }
  493. break loop;
  494. default:
  495. int pivot = (start + end) / 2;
  496. bh = positions[pivot];
  497. if(bh.offset > offset)
  498. end = pivot - 1;
  499. else
  500. start = pivot + 1;
  501. break;
  502. }
  503. }
  504. return start;
  505. } //}}}
  506. //}}}
  507. //{{{ Inner classes
  508. //{{{ PosTopHalf class
  509. static class PosTopHalf implements Position
  510. {
  511. PosBottomHalf bh;
  512. //{{{ PosTopHalf constructor
  513. PosTopHalf(PosBottomHalf bh)
  514. {
  515. this.bh = bh;
  516. bh.ref();
  517. } //}}}
  518. //{{{ getOffset() method
  519. public int getOffset()
  520. {
  521. return bh.offset;
  522. } //}}}
  523. //{{{ finalize() method
  524. public void finalize()
  525. {
  526. bh.unref();
  527. } //}}}
  528. } //}}}
  529. //{{{ PosBottomHalf class
  530. class PosBottomHalf
  531. {
  532. int offset;
  533. int ref;
  534. //{{{ PosBottomHalf constructor
  535. PosBottomHalf(int offset)
  536. {
  537. this.offset = offset;
  538. } //}}}
  539. //{{{ ref() method
  540. void ref()
  541. {
  542. ref++;
  543. } //}}}
  544. //{{{ unref() method
  545. void unref()
  546. {
  547. if(--ref == 0)
  548. removePosition(this);
  549. } //}}}
  550. } //}}}
  551. //}}}
  552. }