PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/bundles/plugins-trunk/BufferLocal/src/ise/plugin/bmp/BufferLocalPlugin.java

#
Java | 642 lines | 443 code | 49 blank | 150 comment | 109 complexity | 8619de731b5644f97552a33fbfa03850 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. package ise.plugin.bmp;
  2. import java.awt.event.WindowEvent;
  3. import java.awt.event.WindowListener;
  4. import java.io.*;
  5. import java.util.*;
  6. import org.gjt.sp.jedit.jEdit;
  7. import org.gjt.sp.jedit.Buffer;
  8. import org.gjt.sp.jedit.EBMessage;
  9. import org.gjt.sp.jedit.EBPlugin;
  10. import org.gjt.sp.jedit.EditPane;
  11. import org.gjt.sp.jedit.View;
  12. import org.gjt.sp.jedit.buffer.FoldHandler;
  13. import org.gjt.sp.jedit.msg.BufferUpdate;
  14. import org.gjt.sp.jedit.msg.EditPaneUpdate;
  15. import org.gjt.sp.jedit.msg.EditorExitRequested;
  16. import org.gjt.sp.jedit.msg.PropertiesChanged;
  17. import org.gjt.sp.jedit.msg.ViewUpdate;
  18. //import org.gjt.sp.util.Log;
  19. /**
  20. * This plugin stores buffer-local properties in a file and restores those
  21. * setting when the file is next opened. The settings are stored as a pipe
  22. * separated string:
  23. * <ul>
  24. * <li>getStringProperty("lineSeparator") Line separator string, values n, r, rn
  25. * <li>buffer.getStringProperty(Buffer.ENCODING) Character encoding string
  26. * <li>buffer.getBooleanProperty(Buffer.GZIPPED) gzip on disk boolean, values t, f
  27. * <li>buffer.getMode().getName() edit mode string
  28. * <li>buffer.getFoldHandler().getName() fold mode string
  29. * <li>buffer.getStringProperty("wrap"); word wrap string
  30. * <li>buffer.getIntProperty("maxLineLength"); wrap width int
  31. * <li>buffer.getIntProperty("tabSize") tab width int
  32. * <li>buffer.getIntProperty("indentSize") indent width int
  33. * <li>buffer.getBooleanProperty("noTabs") soft tabs boolean, t = soft tabs, f = hard tabs
  34. * </ul>
  35. * <p>
  36. * example:n|ISO-8859-1|f|java|indent|none|76|3|3|t
  37. * n|ISO-8859-1|f| | |none|0|4|4|t
  38. * <p>
  39. * TODO: need to check how this works with files loaded with the ftp plugin
  40. * <br>
  41. * DID: seems to work okay with ftp, need to test some more
  42. * <p>
  43. * Jan 5, 2004, per request from Slava: removed
  44. * persistence of line separator and encoding. Kept the string format as above,
  45. * but implementation now does not actually use line separator and encoding
  46. * settings.
  47. *
  48. * This class implements WindowListener, then is attached to each view so that
  49. * the auto-close timer only applies when the window is actually active. If
  50. * the window is deactivated or iconified, the timer is stopped.
  51. *
  52. * @author Dale Anson, danson@germane-software.com
  53. * @version $Revision: 18720 $
  54. * @since Oct 1, 2003
  55. */
  56. public class BufferLocalPlugin extends EBPlugin implements WindowListener {
  57. // runs once every 10 minutes at a low priority to clean up the map
  58. Thread janitor = createJanitorThread();
  59. private Thread createJanitorThread() {
  60. return new Thread() {
  61. public void run() {
  62. setPriority( Thread.MIN_PRIORITY );
  63. while ( true ) {
  64. if ( canClean && map.size() > 0 ) {
  65. synchronized ( map ) {
  66. try {
  67. // do 2 loops to avoid ConcurrentModificationExceptions
  68. List to_remove = new ArrayList();
  69. Iterator it = map.keySet().iterator();
  70. while ( it.hasNext() ) {
  71. String filename = ( String ) it.next();
  72. File f = new File( filename );
  73. if ( !f.exists() ) {
  74. to_remove.add( filename );
  75. }
  76. }
  77. it = to_remove.iterator();
  78. while ( it.hasNext() ) {
  79. map.remove( it.next() );
  80. }
  81. }
  82. catch ( Exception e ) { // NOPMD
  83. // ignored
  84. }
  85. }
  86. }
  87. try {
  88. sleep( ( long ) TEN_MINUTES );
  89. }
  90. catch ( InterruptedException e ) {
  91. // ignored
  92. }
  93. }
  94. }
  95. };
  96. }
  97. // runs once every staleTime minutes at a low priority to auto-close stale buffers
  98. Thread bufferCleaner = createBufferCleanerThread();
  99. private Thread createBufferCleanerThread() {
  100. return new Thread() {
  101. public void run() {
  102. setPriority( Thread.MIN_PRIORITY );
  103. while ( removeStale ) {
  104. try {
  105. if ( canClose ) {
  106. closeFiles();
  107. }
  108. sleep( ( long ) staleTime );
  109. }
  110. catch ( InterruptedException e ) {
  111. // ignored
  112. }
  113. }
  114. }
  115. };
  116. }
  117. private int ONE_MINUTE = 1000 * 60;
  118. private int TEN_MINUTES = ONE_MINUTE * 10;
  119. private int staleTime = 30 * ONE_MINUTE;
  120. private boolean removeStale = false;
  121. // storage for the properties, key is filename as a String,
  122. // value is the property settings String, see above
  123. private Properties map = new Properties();
  124. // temporary storage for properties. Properties are stored here, then moved
  125. // to permanent storage when they actually change.
  126. private Properties tempMap = new Properties();
  127. // storage for open buffers, key is filename as a String,
  128. // value is a BufferReference object
  129. private HashMap<String, BufferReference> openBuffers = new HashMap<String, BufferReference>();
  130. // control for janitor thread.
  131. private boolean canClean;
  132. // control for buffer cleaner thread
  133. private boolean canClose = true;
  134. private Date pausedAt = null;
  135. public static String NAME = "bufferlocal";
  136. private File configFile = null;
  137. /**
  138. * Load the stored buffer local properties. The properties are stored in a
  139. * file named .bufferlocalplugin.cfg in either the jEdit settings directory
  140. * (if writable) otherwise, in $user.home.
  141. */
  142. public void start() {
  143. // load settings from jEdit properties
  144. loadProperties();
  145. // load configuration setings
  146. // Previously, this file was stored either in the jEdit settings directory
  147. // or user.home. Now it is stored in plugin home. For backward compatibility,
  148. // first check plugin home. If the config file is there, assume it has already
  149. // been migrated. If not, check settings directory and user home and copy it
  150. // to plugin home, then use it. Once done, delete the old file.
  151. try {
  152. File homeDir = jEdit.getPlugin( "ise.plugin.bmp.BufferLocalPlugin" ).getPluginHome();
  153. homeDir.mkdir();
  154. configFile = new File( homeDir, ".bufferlocalplugin.cfg" );
  155. if ( configFile.exists() ) {
  156. BufferedInputStream in = new BufferedInputStream( new FileInputStream( configFile ) );
  157. map.load( in );
  158. in.close();
  159. }
  160. else {
  161. String oldDir = jEdit.getSettingsDirectory();
  162. if ( oldDir == null ) {
  163. oldDir = System.getProperty( "user.home" );
  164. }
  165. configFile = new File( oldDir, ".bufferlocalplugin.cfg" );
  166. if ( configFile.exists() ) {
  167. BufferedInputStream in = new BufferedInputStream( new FileInputStream( configFile ) );
  168. map.load( in );
  169. in.close();
  170. // delete the old file and write out the new file
  171. configFile.delete();
  172. configFile = new File( homeDir, ".bufferlocalplugin.cfg" );
  173. synchronized ( map ) {
  174. BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( configFile ) );
  175. map.store( out, "Machine generated for BufferLocalPlugin, DO NOT EDIT!" );
  176. out.flush();
  177. out.close();
  178. }
  179. }
  180. }
  181. }
  182. catch (Exception e) { // NOPMD
  183. // ignored
  184. }
  185. // TODO: attach to open Views
  186. for ( View view : jEdit.getViews() ) {
  187. view.addWindowListener( this );
  188. }
  189. // prep for currently open buffers
  190. initOpenBuffers();
  191. // start janitor and cleaner threads
  192. canClean = true;
  193. restartThreads();
  194. }
  195. private void restartThreads() {
  196. if ( janitor.isAlive() ) {
  197. janitor.interrupt();
  198. }
  199. janitor = createJanitorThread();
  200. janitor.start();
  201. if ( bufferCleaner.isAlive() ) {
  202. bufferCleaner.interrupt();
  203. }
  204. bufferCleaner = createBufferCleanerThread();
  205. bufferCleaner.start();
  206. }
  207. /**
  208. * Save the buffer local properties to disk.
  209. *
  210. * @see start
  211. */
  212. public void stop() {
  213. canClean = false;
  214. janitor.interrupt();
  215. bufferCleaner.interrupt();
  216. if ( configFile == null || !configFile.exists() ) {
  217. File homeDir = jEdit.getPlugin( "ise.plugin.bmp.BufferLocalPlugin" ).getPluginHome();
  218. homeDir.mkdir();
  219. configFile = new File( homeDir, ".bufferlocalplugin.cfg" );
  220. }
  221. try {
  222. synchronized ( map ) {
  223. BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( configFile ) );
  224. map.store( out, "Machine generated for BufferLocalPlugin, DO NOT EDIT!" );
  225. out.flush();
  226. out.close();
  227. }
  228. }
  229. catch ( Exception e ) { // NOPMD
  230. // ignored
  231. }
  232. // detach from open views
  233. for ( View view : jEdit.getViews() ) {
  234. view.removeWindowListener( this );
  235. }
  236. }
  237. /**
  238. * Check for BufferUpdate messages. Save properties on CLOSED, restore
  239. * properties on LOADED.
  240. *
  241. * @param message
  242. */
  243. public void handleMessage( EBMessage message ) {
  244. if ( message instanceof BufferUpdate ) {
  245. BufferUpdate bu = ( BufferUpdate ) message;
  246. Object what = bu.getWhat();
  247. Buffer buffer = bu.getBuffer();
  248. if ( buffer == null ) {
  249. return ;
  250. }
  251. String file = buffer.getPath();
  252. if ( BufferUpdate.LOADED.equals( what ) || BufferUpdate.SAVED.equals( what ) ) {
  253. String props = map.getProperty( file );
  254. if ( props != null ) {
  255. // parse the stored properties
  256. String[] tokens = props.split( "[\\|]" );
  257. String ls = tokens[ 0 ];
  258. String enc = tokens[ 1 ];
  259. String gz = tokens[ 2 ];
  260. String em = tokens[ 3 ];
  261. String fm = tokens[ 4 ];
  262. String wm = tokens[ 5 ];
  263. String ll = tokens[ 6 ];
  264. String tw = tokens[ 7 ];
  265. String iw = tokens[ 8 ];
  266. String tabs = tokens[ 9 ];
  267. // apply the stored properties to the buffer
  268. /// see comments above, don't need this right now
  269. /// 13 Dec 2005, more comments, looks like there is a use case for this
  270. /// stuff after all
  271. if ( "n".equals( ls ) ) {
  272. ls = "\n";
  273. }
  274. else if ( "r".equals( ls ) ) {
  275. ls = "\r";
  276. }
  277. else {
  278. ls = "\r\n";
  279. }
  280. buffer.setStringProperty( "lineSeparator", ls );
  281. buffer.setStringProperty( Buffer.ENCODING, enc );
  282. ///
  283. if ( gz != null && gz.length() > 0 )
  284. buffer.setBooleanProperty( Buffer.GZIPPED, "t".equals( gz ) ? true : false );
  285. if ( fm != null && fm.length() > 0 && FoldHandler.getFoldHandler( fm ) != null )
  286. buffer.setFoldHandler( FoldHandler.getFoldHandler( fm ) );
  287. if ( wm != null && wm.length() > 0 )
  288. buffer.setStringProperty( "wrap", wm );
  289. if ( ll != null && ll.length() > 0 )
  290. buffer.setIntegerProperty( "maxLineLength", Integer.parseInt( ll ) );
  291. if ( tw != null && tw.length() > 0 )
  292. buffer.setIntegerProperty( "tabSize", Integer.parseInt( tw ) );
  293. if ( iw != null && iw.length() > 0 )
  294. buffer.setIntegerProperty( "indentSize", Integer.parseInt( iw ) );
  295. if ( tabs != null && tabs.length() > 0 )
  296. buffer.setBooleanProperty( "noTabs", "t".equals( tabs ) ? true : false );
  297. if ( em != null && em.length() > 0 )
  298. buffer.setMode( em );
  299. }
  300. else {
  301. // on load, if we don't already have properties stored for this file,
  302. // stash the string to check against when the file is closed.
  303. tempMap.setProperty( file, getBufferLocalString( buffer ) );
  304. }
  305. View view = bu.getView();
  306. if ( view == null ) {
  307. view = jEdit.getActiveView();
  308. }
  309. openBuffers.put( buffer.getPath(), new BufferReference( view, buffer ) );
  310. }
  311. else if ( BufferUpdate.CLOSED.equals( what ) || BufferUpdate.PROPERTIES_CHANGED.equals( what ) ) {
  312. String bufferLocalString = getBufferLocalString(buffer);
  313. map.setProperty( file, bufferLocalString );
  314. if ( BufferUpdate.CLOSED.equals( what ) ) {
  315. openBuffers.remove( buffer.getPath() );
  316. }
  317. }
  318. }
  319. else if ( message instanceof EditorExitRequested ) {
  320. // jEdit may be shutting down, so update the map for any buffers still open.
  321. // Oddly enough, jEdit doesn't send CLOSED messages as it closes buffers
  322. // during shutdown. Or maybe it does, but this plugin is unloaded before
  323. // getting those messages.
  324. Buffer[] buffers = jEdit.getBuffers();
  325. String file;
  326. String props;
  327. String tempProps;
  328. for ( int i = 0; i < buffers.length; i++ ) {
  329. file = buffers[ i ].getPath();
  330. // only save if changed, no need to save if not. Doing it this way
  331. // rather than on PROPERTY_CHANGED as jEdit sends lots of
  332. // PROPERTY_CHANGED messages even though the properties really
  333. // haven't changed
  334. props = getBufferLocalString( buffers[ i ] );
  335. tempProps = tempMap.getProperty( file );
  336. if ( tempProps == null ) {
  337. continue;
  338. }
  339. if ( !props.equals( tempProps ) ) {
  340. map.setProperty( file, props );
  341. }
  342. }
  343. }
  344. else if ( message instanceof EditPaneUpdate ) {
  345. // populate the openBuffers list
  346. EditPaneUpdate epu = ( EditPaneUpdate ) message;
  347. Object what = epu.getWhat();
  348. if ( EditPaneUpdate.BUFFER_CHANGED.equals( what ) ) {
  349. View view = epu.getEditPane().getView();
  350. Buffer buffer = epu.getEditPane().getBuffer();
  351. openBuffers.put( buffer.getPath(), new BufferReference( view, buffer ) );
  352. }
  353. }
  354. else if ( message instanceof ViewUpdate ) {
  355. ViewUpdate vu = ( ViewUpdate ) message;
  356. if ( ViewUpdate.CREATED.equals( vu.getWhat() ) ) {
  357. initView( vu.getView() );
  358. vu.getView().addWindowListener( this );
  359. }
  360. else if ( ViewUpdate.CLOSED.equals( vu.getWhat() ) ) {
  361. vu.getView().removeWindowListener( this );
  362. }
  363. }
  364. else if ( message instanceof PropertiesChanged ) {
  365. loadProperties();
  366. }
  367. }
  368. /** Closes stale files right now. */
  369. public void closeFiles() {
  370. if ( openBuffers.size() > 0 ) {
  371. synchronized ( openBuffers ) {
  372. try {
  373. // do 2 loops to avoid ConcurrentModificationExceptions
  374. Calendar stale_time = Calendar.getInstance();
  375. stale_time.add( Calendar.MILLISECOND, -staleTime );
  376. Iterator it = openBuffers.keySet().iterator();
  377. List to_close = new ArrayList();
  378. while ( it.hasNext() ) {
  379. String path = ( String ) it.next();
  380. BufferReference br = ( BufferReference ) openBuffers.get( path );
  381. Calendar viewed = br.getViewed();
  382. View view = br.getView();
  383. if ( view == null ) {
  384. view = jEdit.getActiveView();
  385. }
  386. Buffer buffer = br.getBuffer();
  387. if ( buffer.equals( view.getEditPane().getBuffer() ) ) {
  388. // don't close the current buffer, even though it may
  389. // been open for more than stale time
  390. continue;
  391. }
  392. if ( buffer.isDirty() ) {
  393. continue; // don't auto-close a dirty buffer
  394. }
  395. if ( viewed.before( stale_time ) ) {
  396. to_close.add( br );
  397. }
  398. }
  399. it = to_close.iterator();
  400. while ( it.hasNext() ) {
  401. BufferReference br = ( BufferReference ) it.next();
  402. View view = br.getView();
  403. if ( view == null ) {
  404. view = jEdit.getActiveView();
  405. }
  406. Buffer buffer = br.getBuffer();
  407. if ( jEdit.closeBuffer( view, buffer ) ) {
  408. openBuffers.remove( buffer.getPath() );
  409. }
  410. }
  411. }
  412. catch ( Exception e ) {
  413. e.printStackTrace();
  414. // ignored
  415. }
  416. }
  417. }
  418. }
  419. /**
  420. * @param buffer the buffer to get properties for
  421. * @return a string representing the buffer-local properties. See
  422. * explanation and example of string at the top of this file.
  423. */
  424. private String getBufferLocalString( Buffer buffer ) {
  425. // get the properties
  426. String ls = buffer.getStringProperty( "lineSeparator" );
  427. String enc = buffer.getStringProperty( Buffer.ENCODING );
  428. boolean gz = buffer.getBooleanProperty( Buffer.GZIPPED );
  429. String em;
  430. if ( buffer.getMode() != null )
  431. em = buffer.getMode().getName();
  432. else
  433. em = "";
  434. String fm = buffer.getFoldHandler() == null ? "" : buffer.getFoldHandler().getName();
  435. String wm = buffer.getStringProperty( "wrap" );
  436. int ll = buffer.getIntegerProperty( "maxLineLength", 0 );
  437. int tw = buffer.getIntegerProperty( "tabSize", 3 );
  438. int iw = buffer.getIntegerProperty( "indentSize", 3 );
  439. boolean tabs = buffer.getBooleanProperty( "noTabs" );
  440. // build the string
  441. StringBuffer prop = new StringBuffer();
  442. if ( "\n".equals( ls ) ) {
  443. prop.append( "n|" );
  444. }
  445. else if ( "\r".equals( ls ) ) {
  446. prop.append( "r|" );
  447. }
  448. else {
  449. prop.append( "rn|" );
  450. }
  451. prop.append( enc ).append( '|' );
  452. prop.append( gz ? "t|" : "f|" );
  453. prop.append( em ).append( '|' );
  454. prop.append( fm ).append( '|' );
  455. prop.append( wm ).append( '|' );
  456. prop.append( String.valueOf( ll ) ).append( '|' );
  457. prop.append( String.valueOf( tw ) ).append( '|' );
  458. prop.append( String.valueOf( iw ) ).append( '|' );
  459. prop.append( tabs ? 't' : 'f' );
  460. return prop.toString();
  461. }
  462. /**
  463. * Reads the properties for this plugin from the jEdit properties. Currently,
  464. * there are 2 properties, "bufferlocal.staleTime", which is the number of
  465. * minutes that a file can remain open without being used before it will be
  466. * closed, and "bufferlocal.removeStale" which is a boolean to decide if
  467. * stale files should be closed after the staleTime has been reached.
  468. */
  469. private void loadProperties() {
  470. int newStaleTime = jEdit.getIntegerProperty( NAME + ".staleTime", staleTime ) * ONE_MINUTE;
  471. boolean newRemoveStale = jEdit.getBooleanProperty( NAME + ".removeStale", removeStale );
  472. if ( newStaleTime != staleTime || newRemoveStale != removeStale ) {
  473. staleTime = newStaleTime;
  474. removeStale = newRemoveStale;
  475. restartThreads();
  476. }
  477. }
  478. /**
  479. * Gets a list of the currently open buffers and populates openBuffers.
  480. */
  481. private void initOpenBuffers() {
  482. for ( View view : jEdit.getViews() ) {
  483. initView( view );
  484. }
  485. }
  486. private void initView( View view ) {
  487. for ( EditPane editPane : view.getEditPanes() ) {
  488. for ( Buffer buffer : editPane.getBufferSet().getAllBuffers() ) {
  489. BufferReference br = new BufferReference( view, buffer );
  490. openBuffers.put( buffer.getPath(), br );
  491. }
  492. }
  493. }
  494. /**
  495. * A data object to track buffers
  496. * @version $Revision: 18720 $
  497. */
  498. public class BufferReference {
  499. private View view;
  500. private Buffer buffer;
  501. private Calendar viewed;
  502. /**
  503. * Constructor for BufferReference
  504. *
  505. * @param view
  506. * @param buffer
  507. */
  508. public BufferReference( View view, Buffer buffer ) {
  509. this.view = view;
  510. this.buffer = buffer;
  511. viewed = Calendar.getInstance();
  512. }
  513. /** Sets the viewed attribute of the BufferReference object */
  514. public void setViewed() {
  515. viewed = Calendar.getInstance();
  516. }
  517. /**
  518. * Gets the view attribute of the BufferReference object
  519. *
  520. * @return The view value
  521. */
  522. public View getView() {
  523. return view;
  524. }
  525. /**
  526. * Gets the buffer attribute of the BufferReference object
  527. *
  528. * @return The buffer value
  529. */
  530. public Buffer getBuffer() {
  531. return buffer;
  532. }
  533. /**
  534. * Gets the viewed attribute of the BufferReference object
  535. *
  536. * @return The viewed value
  537. */
  538. public Calendar getViewed() {
  539. return viewed;
  540. }
  541. /**
  542. * @return Description of the Returned Value
  543. */
  544. public String toString() {
  545. StringBuffer sb = new StringBuffer( 50 );
  546. sb.append( "BufferReference[" );
  547. sb.append( buffer.getPath() ).append( ',' );
  548. sb.append( viewed.getTime().toString() ).append( ']' );
  549. return sb.toString();
  550. }
  551. }
  552. private void adjustForPause() {
  553. if ( jEdit.getBooleanProperty( "bufferlocal.whileActive" ) && pausedAt != null ) {
  554. Date now = new Date();
  555. long pausedFor = now.getTime() - pausedAt.getTime();
  556. pausedAt = null;
  557. for ( BufferReference br : openBuffers.values() ) {
  558. br.getViewed().add( Calendar.MILLISECOND, ( int ) pausedFor );
  559. }
  560. }
  561. }
  562. public void windowActivated( WindowEvent e ) {
  563. adjustForPause();
  564. canClose = true;
  565. }
  566. public void windowClosed( WindowEvent e ) {
  567. canClose = false;
  568. pausedAt = new Date();
  569. }
  570. public void windowDeactivated( WindowEvent e ) {
  571. canClose = false;
  572. pausedAt = new Date();
  573. }
  574. public void windowDeiconified( WindowEvent e ) {
  575. adjustForPause();
  576. canClose = true;
  577. }
  578. public void windowIconified( WindowEvent e ) {
  579. canClose = false;
  580. pausedAt = new Date();
  581. }
  582. public void windowGainedFocus( WindowEvent e ) {
  583. adjustForPause();
  584. canClose = true;
  585. }
  586. public void windowLostFocus( WindowEvent e ) {
  587. canClose = false;
  588. pausedAt = new Date();
  589. }
  590. public void windowClosing( WindowEvent e ) {}
  591. public void windowOpened( WindowEvent e ) {}
  592. public void windowStateChanged( WindowEvent e ) {}
  593. }