/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/io/VFS.java

# · Java · 1112 lines · 471 code · 103 blank · 538 comment · 71 complexity · 4ae4b3a6202bbccdbb91a55cce26d3ba MD5 · raw file

  1. /*
  2. * VFS.java - Virtual filesystem implementation
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2000, 2003 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.io;
  23. //{{{ Imports
  24. import java.awt.Color;
  25. import java.awt.Component;
  26. import java.io.*;
  27. import java.util.*;
  28. import java.util.regex.Pattern;
  29. import java.util.regex.PatternSyntaxException;
  30. import org.gjt.sp.jedit.buffer.*;
  31. import org.gjt.sp.jedit.msg.PropertiesChanged;
  32. import org.gjt.sp.jedit.*;
  33. import org.gjt.sp.util.Log;
  34. import org.gjt.sp.util.ProgressObserver;
  35. import org.gjt.sp.util.IOUtilities;
  36. //}}}
  37. /**
  38. * A virtual filesystem implementation.<p>
  39. *
  40. * Plugins can provide virtual file systems by defining entries in their
  41. * <code>services.xml</code> files like so:
  42. *
  43. * <pre>&lt;SERVICE CLASS="org.gjt.sp.jedit.io.VFS" NAME="<i>name</i>"&gt;
  44. * new <i>MyVFS</i>();
  45. *&lt;/SERVICE&gt;</pre>
  46. *
  47. * URLs of the form <code><i>name</i>:<i>path</i></code> will then be handled
  48. * by the VFS named <code><i>name</i></code>.<p>
  49. *
  50. * See {@link org.gjt.sp.jedit.ServiceManager} for details.<p>
  51. *
  52. * <h3>Session objects:</h3>
  53. *
  54. * A session is used to persist things like login information, any network
  55. * sockets, etc. File system implementations that do not need this kind of
  56. * persistence return a dummy object as a session.<p>
  57. *
  58. * Methods whose names are prefixed with "_" expect to be given a
  59. * previously-obtained session object. A session must be obtained from the AWT
  60. * thread in one of two ways:
  61. *
  62. * <ul>
  63. * <li>{@link #createVFSSession(String,Component)}</li>
  64. * <li>{@link #showBrowseDialog(Object[],Component)}</li>
  65. * </ul>
  66. *
  67. * When done, the session must be disposed of using
  68. * {@link #_endVFSSession(Object,Component)}.<p>
  69. *
  70. * <h3>Thread safety:</h3>
  71. *
  72. * The following methods cannot be called from an I/O thread:
  73. *
  74. * <ul>
  75. * <li>{@link #createVFSSession(String,Component)}</li>
  76. * <li>{@link #insert(View,Buffer,String)}</li>
  77. * <li>{@link #load(View,Buffer,String)}</li>
  78. * <li>{@link #save(View,Buffer,String)}</li>
  79. * <li>{@link #showBrowseDialog(Object[],Component)}</li>
  80. * </ul>
  81. *
  82. * All remaining methods are required to be thread-safe in subclasses.
  83. *
  84. * <h3>Implementing a VFS</h3>
  85. *
  86. * You can override as many or as few methods as you want. Make sure
  87. * {@link #getCapabilities()} returns a value reflecting the functionality
  88. * implemented by your VFS.
  89. *
  90. * @see VFSManager#getVFSForPath(String)
  91. * @see VFSManager#getVFSForProtocol(String)
  92. *
  93. * @author Slava Pestov
  94. * @author $Id: VFS.java 5490 2006-06-24 09:22:43Z kpouer $
  95. */
  96. public abstract class VFS
  97. {
  98. //{{{ Capabilities
  99. /**
  100. * Read capability.
  101. * @since jEdit 2.6pre2
  102. */
  103. public static final int READ_CAP = 1 << 0;
  104. /**
  105. * Write capability.
  106. * @since jEdit 2.6pre2
  107. */
  108. public static final int WRITE_CAP = 1 << 1;
  109. /**
  110. * @deprecated Do not define this capability.<p>
  111. *
  112. * This was the official API for adding items to a file
  113. * system browser's <b>Plugins</b> menu in jEdit 4.1 and earlier. In
  114. * jEdit 4.2, there is a different way of doing this, you must provide
  115. * a <code>browser.actions.xml</code> file in your plugin JAR, and
  116. * define <code>plugin.<i>class</i>.browser-menu-item</code>
  117. * or <code>plugin.<i>class</i>.browser-menu</code> properties.
  118. * See {@link org.gjt.sp.jedit.EditPlugin} for details.
  119. */
  120. public static final int BROWSE_CAP = 1 << 2;
  121. /**
  122. * Delete file capability.
  123. * @since jEdit 2.6pre2
  124. */
  125. public static final int DELETE_CAP = 1 << 3;
  126. /**
  127. * Rename file capability.
  128. * @since jEdit 2.6pre2
  129. */
  130. public static final int RENAME_CAP = 1 << 4;
  131. /**
  132. * Make directory capability.
  133. * @since jEdit 2.6pre2
  134. */
  135. public static final int MKDIR_CAP = 1 << 5;
  136. /**
  137. * Low latency capability. If this is not set, then a confirm dialog
  138. * will be shown before doing a directory search in this VFS.
  139. * @since jEdit 4.1pre1
  140. */
  141. public static final int LOW_LATENCY_CAP = 1 << 6;
  142. /**
  143. * Case insensitive file system capability.
  144. * @since jEdit 4.1pre1
  145. */
  146. public static final int CASE_INSENSITIVE_CAP = 1 << 7;
  147. //}}}
  148. //{{{ Extended attributes
  149. /**
  150. * File type.
  151. * @since jEdit 4.2pre1
  152. */
  153. public static final String EA_TYPE = "type";
  154. /**
  155. * File status (read only, read write, etc).
  156. * @since jEdit 4.2pre1
  157. */
  158. public static final String EA_STATUS = "status";
  159. /**
  160. * File size.
  161. * @since jEdit 4.2pre1
  162. */
  163. public static final String EA_SIZE = "size";
  164. /**
  165. * File last modified date.
  166. * @since jEdit 4.2pre1
  167. */
  168. public static final String EA_MODIFIED = "modified";
  169. //}}}
  170. //{{{ VFS constructor
  171. /**
  172. * @deprecated Use the form where the constructor takes a capability
  173. * list.
  174. */
  175. public VFS(String name)
  176. {
  177. this(name,0);
  178. } //}}}
  179. //{{{ VFS constructor
  180. /**
  181. * Creates a new virtual filesystem.
  182. * @param name The name
  183. * @param caps The capabilities
  184. */
  185. public VFS(String name, int caps)
  186. {
  187. this.name = name;
  188. this.caps = caps;
  189. // reasonable defaults (?)
  190. this.extAttrs = new String[] { EA_SIZE, EA_TYPE };
  191. } //}}}
  192. //{{{ VFS constructor
  193. /**
  194. * Creates a new virtual filesystem.
  195. * @param name The name
  196. * @param caps The capabilities
  197. * @param extAttrs The extended attributes
  198. * @since jEdit 4.2pre1
  199. */
  200. public VFS(String name, int caps, String[] extAttrs)
  201. {
  202. this.name = name;
  203. this.caps = caps;
  204. this.extAttrs = extAttrs;
  205. } //}}}
  206. //{{{ getName() method
  207. /**
  208. * Returns this VFS's name. The name is used to obtain the
  209. * label stored in the <code>vfs.<i>name</i>.label</code>
  210. * property.
  211. */
  212. public String getName()
  213. {
  214. return name;
  215. } //}}}
  216. //{{{ getCapabilities() method
  217. /**
  218. * Returns the capabilities of this VFS.
  219. * @since jEdit 2.6pre2
  220. */
  221. public int getCapabilities()
  222. {
  223. return caps;
  224. } //}}}
  225. //{{{ getExtendedAttributes() method
  226. /**
  227. * Returns the extended attributes supported by this VFS.
  228. * @since jEdit 4.2pre1
  229. */
  230. public String[] getExtendedAttributes()
  231. {
  232. return extAttrs;
  233. } //}}}
  234. //{{{ showBrowseDialog() method
  235. /**
  236. * Displays a dialog box that should set up a session and return
  237. * the initial URL to browse.
  238. * @param session Where the VFS session will be stored
  239. * @param comp The component that will parent error dialog boxes
  240. * @return The URL
  241. * @since jEdit 2.7pre1
  242. */
  243. public String showBrowseDialog(Object[] session, Component comp)
  244. {
  245. return null;
  246. } //}}}
  247. //{{{ getFileName() method
  248. /**
  249. * Returns the file name component of the specified path.
  250. * @param path The path
  251. * @since jEdit 3.1pre4
  252. */
  253. public String getFileName(String path)
  254. {
  255. if(path.equals("/"))
  256. return path;
  257. while(path.endsWith("/") || path.endsWith(File.separator))
  258. path = path.substring(0,path.length() - 1);
  259. int index = Math.max(path.lastIndexOf('/'),
  260. path.lastIndexOf(File.separatorChar));
  261. if(index == -1)
  262. index = path.indexOf(':');
  263. // don't want getFileName("roots:") to return ""
  264. if(index == -1 || index == path.length() - 1)
  265. return path;
  266. return path.substring(index + 1);
  267. } //}}}
  268. //{{{ getParentOfPath() method
  269. /**
  270. * Returns the parent of the specified path. This must be
  271. * overridden to return a non-null value for browsing of this
  272. * filesystem to work.
  273. * @param path The path
  274. * @since jEdit 2.6pre5
  275. */
  276. public String getParentOfPath(String path)
  277. {
  278. // ignore last character of path to properly handle
  279. // paths like /foo/bar/
  280. int lastIndex = path.length() - 1;
  281. while(lastIndex > 0
  282. && (path.charAt(lastIndex) == File.separatorChar
  283. || path.charAt(lastIndex) == '/'))
  284. {
  285. lastIndex--;
  286. }
  287. int count = Math.max(0,lastIndex);
  288. int index = path.lastIndexOf(File.separatorChar,count);
  289. if(index == -1)
  290. index = path.lastIndexOf('/',count);
  291. if(index == -1)
  292. {
  293. // this ensures that getFileParent("protocol:"), for
  294. // example, is "protocol:" and not "".
  295. index = path.lastIndexOf(':');
  296. }
  297. return path.substring(0,index + 1);
  298. } //}}}
  299. //{{{ constructPath() method
  300. /**
  301. * Constructs a path from the specified directory and
  302. * file name component. This must be overridden to return a
  303. * non-null value, otherwise browsing this filesystem will
  304. * not work.<p>
  305. *
  306. * Unless you are writing a VFS, this method should not be called
  307. * directly. To ensure correct behavior, you <b>must</b> call
  308. * {@link org.gjt.sp.jedit.MiscUtilities#constructPath(String,String)}
  309. * instead.
  310. *
  311. * @param parent The parent directory
  312. * @param path The path
  313. * @since jEdit 2.6pre2
  314. */
  315. public String constructPath(String parent, String path)
  316. {
  317. return parent + path;
  318. } //}}}
  319. //{{{ getFileSeparator() method
  320. /**
  321. * Returns the file separator used by this VFS.
  322. * @since jEdit 2.6pre9
  323. */
  324. public char getFileSeparator()
  325. {
  326. return '/';
  327. } //}}}
  328. //{{{ getTwoStageSaveName() method
  329. /**
  330. * Returns a temporary file name based on the given path.
  331. *
  332. * By default jEdit first saves a file to <code>#<i>name</i>#save#</code>
  333. * and then renames it to the original file. However some virtual file
  334. * systems might not support the <code>#</code> character in filenames,
  335. * so this method permits the VFS to override this behavior.
  336. *
  337. * If this method returns <code>null</code>, two stage save will not
  338. * be used for that particular file (introduced in jEdit 4.3pre1).
  339. *
  340. * @param path The path name
  341. * @since jEdit 4.1pre7
  342. */
  343. public String getTwoStageSaveName(String path)
  344. {
  345. return MiscUtilities.constructPath(getParentOfPath(path),
  346. '#' + getFileName(path) + "#save#");
  347. } //}}}
  348. //{{{ reloadDirectory() method
  349. /**
  350. * Called before a directory is reloaded by the file system browser.
  351. * Can be used to flush a cache, etc.
  352. * @since jEdit 4.0pre3
  353. */
  354. public void reloadDirectory(String path) {} //}}}
  355. //{{{ createVFSSession() method
  356. /**
  357. * Creates a VFS session. This method is called from the AWT thread,
  358. * so it should not do any I/O. It could, however, prompt for
  359. * a login name and password, for example.
  360. * @param path The path in question
  361. * @param comp The component that will parent any dialog boxes shown
  362. * @return The session
  363. * @since jEdit 2.6pre3
  364. */
  365. public Object createVFSSession(String path, Component comp)
  366. {
  367. return new Object();
  368. } //}}}
  369. //{{{ load() method
  370. /**
  371. * Loads the specified buffer. The default implementation posts
  372. * an I/O request to the I/O thread.
  373. * @param view The view
  374. * @param buffer The buffer
  375. * @param path The path
  376. */
  377. public boolean load(View view, Buffer buffer, String path)
  378. {
  379. if((getCapabilities() & READ_CAP) == 0)
  380. {
  381. VFSManager.error(view,path,"vfs.not-supported.load",new String[] { name });
  382. return false;
  383. }
  384. Object session = createVFSSession(path,view);
  385. if(session == null)
  386. return false;
  387. if((getCapabilities() & WRITE_CAP) == 0)
  388. buffer.setReadOnly(true);
  389. BufferIORequest request = new BufferLoadRequest(
  390. view,buffer,session,this,path);
  391. if(buffer.isTemporary())
  392. // this makes HyperSearch much faster
  393. request.run();
  394. else
  395. VFSManager.runInWorkThread(request);
  396. return true;
  397. } //}}}
  398. //{{{ save() method
  399. /**
  400. * Saves the specifies buffer. The default implementation posts
  401. * an I/O request to the I/O thread.
  402. * @param view The view
  403. * @param buffer The buffer
  404. * @param path The path
  405. */
  406. public boolean save(View view, Buffer buffer, String path)
  407. {
  408. if((getCapabilities() & WRITE_CAP) == 0)
  409. {
  410. VFSManager.error(view,path,"vfs.not-supported.save",new String[] { name });
  411. return false;
  412. }
  413. Object session = createVFSSession(path,view);
  414. if(session == null)
  415. return false;
  416. /* When doing a 'save as', the path to save to (path)
  417. * will not be the same as the buffer's previous path
  418. * (buffer.getPath()). In that case, we want to create
  419. * a backup of the new path, even if the old path was
  420. * backed up as well (BACKED_UP property set) */
  421. if(!path.equals(buffer.getPath()))
  422. buffer.unsetProperty(Buffer.BACKED_UP);
  423. VFSManager.runInWorkThread(new BufferSaveRequest(
  424. view,buffer,session,this,path));
  425. return true;
  426. } //}}}
  427. //{{{ copy() method
  428. /**
  429. * Copy a file to another using VFS.
  430. *
  431. * @param progress the progress observer. It could be null if you don't want to monitor progress. If not null
  432. * you should probably launch this command in a WorkThread
  433. * @param sourceVFS the source VFS
  434. * @param sourceSession the VFS session
  435. * @param sourcePath the source path
  436. * @param targetVFS the target VFS
  437. * @param targetSession the target session
  438. * @param targetPath the target path
  439. * @param comp comp The component that will parent error dialog boxes
  440. * @return true if the copy was successful
  441. * @throws IOException IOException If an I/O error occurs
  442. * @since jEdit 4.3pre3
  443. */
  444. public static boolean copy(ProgressObserver progress, VFS sourceVFS, Object sourceSession,String sourcePath,
  445. VFS targetVFS, Object targetSession,String targetPath, Component comp, boolean canStop)
  446. throws IOException
  447. {
  448. if (progress != null)
  449. progress.setStatus("Initializing");
  450. InputStream in = null;
  451. OutputStream out = null;
  452. try
  453. {
  454. if (progress != null)
  455. {
  456. VFSFile sourceVFSFile = sourceVFS._getFile(sourceSession, sourcePath, comp);
  457. if (sourceVFSFile == null)
  458. throw new FileNotFoundException(sourcePath);
  459. progress.setMaximum(sourceVFSFile.getLength());
  460. }
  461. in = new BufferedInputStream(sourceVFS._createInputStream(sourceSession, sourcePath, false, comp));
  462. out = new BufferedOutputStream(targetVFS._createOutputStream(targetSession, targetPath, comp));
  463. boolean copyResult = IOUtilities.copyStream(4096, progress, in, out, canStop);
  464. VFSManager.sendVFSUpdate(targetVFS, targetPath, true);
  465. return copyResult;
  466. }
  467. finally
  468. {
  469. IOUtilities.closeQuietly(in);
  470. IOUtilities.closeQuietly(out);
  471. }
  472. } //}}}
  473. //{{{ copy() method
  474. /**
  475. * Copy a file to another using VFS.
  476. *
  477. * @param progress the progress observer. It could be null if you don't want to monitor progress. If not null
  478. * you should probably launch this command in a WorkThread
  479. * @param sourcePath the source path
  480. * @param targetPath the target path
  481. * @param comp comp The component that will parent error dialog boxes
  482. * @param canStop if true the copy can be stopped
  483. * @return true if the copy was successful
  484. * @throws IOException IOException If an I/O error occurs
  485. * @since jEdit 4.3pre3
  486. */
  487. public static boolean copy(ProgressObserver progress, String sourcePath,String targetPath, Component comp, boolean canStop)
  488. throws IOException
  489. {
  490. VFS sourceVFS = VFSManager.getVFSForPath(sourcePath);
  491. Object sourceSession = sourceVFS.createVFSSession(sourcePath, comp);
  492. VFS targetVFS = VFSManager.getVFSForPath(targetPath);
  493. Object targetSession = targetVFS.createVFSSession(targetPath, comp);
  494. return copy(progress, sourceVFS, sourceSession, sourcePath, targetVFS, targetSession, targetPath, comp,canStop);
  495. } //}}}
  496. //{{{ insert() method
  497. /**
  498. * Inserts a file into the specified buffer. The default implementation
  499. * posts an I/O request to the I/O thread.
  500. * @param view The view
  501. * @param buffer The buffer
  502. * @param path The path
  503. */
  504. public boolean insert(View view, Buffer buffer, String path)
  505. {
  506. if((getCapabilities() & READ_CAP) == 0)
  507. {
  508. VFSManager.error(view,path,"vfs.not-supported.load",new String[] { name });
  509. return false;
  510. }
  511. Object session = createVFSSession(path,view);
  512. if(session == null)
  513. return false;
  514. VFSManager.runInWorkThread(new BufferInsertRequest(
  515. view,buffer,session,this,path));
  516. return true;
  517. } //}}}
  518. // A method name that starts with _ requires a session object
  519. //{{{ _canonPath() method
  520. /**
  521. * Returns the canonical form of the specified path name. For example,
  522. * <code>~</code> might be expanded to the user's home directory.
  523. * @param session The session
  524. * @param path The path
  525. * @param comp The component that will parent error dialog boxes
  526. * @exception IOException if an I/O error occurred
  527. * @since jEdit 4.0pre2
  528. */
  529. public String _canonPath(Object session, String path, Component comp)
  530. throws IOException
  531. {
  532. return path;
  533. } //}}}
  534. //{{{ _listDirectory() method
  535. /**
  536. * A convinience method that matches file names against globs, and can
  537. * optionally list the directory recursively.
  538. * @param session The session
  539. * @param directory The directory. Note that this must be a full
  540. * URL, including the host name, path name, and so on. The
  541. * username and password (if needed by the VFS) is obtained from the
  542. * session instance.
  543. * @param glob Only file names matching this glob will be returned
  544. * @param recursive If true, subdirectories will also be listed.
  545. * @param comp The component that will parent error dialog boxes
  546. * @exception IOException if an I/O error occurred
  547. * @since jEdit 4.1pre1
  548. */
  549. public String[] _listDirectory(Object session, String directory,
  550. String glob, boolean recursive, Component comp )
  551. throws IOException
  552. {
  553. String[] retval = null;
  554. retval = _listDirectory(session, directory, glob, recursive, comp, true, false);
  555. return retval;
  556. }
  557. //{{{ _listDirectory() method
  558. /**
  559. * A convinience method that matches file names against globs, and can
  560. * optionally list the directory recursively.
  561. * @param session The session
  562. * @param directory The directory. Note that this must be a full
  563. * URL, including the host name, path name, and so on. The
  564. * username and password (if needed by the VFS) is obtained from the
  565. * session instance.
  566. * @param glob Only file names matching this glob will be returned
  567. * @param recursive If true, subdirectories will also be listed.
  568. * @param comp The component that will parent error dialog boxes
  569. * @exception IOException if an I/O error occurred
  570. * @param skipBinary ignore binary files (do not return them).
  571. * This will slow down the process since it will open the files
  572. * @param skipHidden skips hidden files, directories, and
  573. * backup files. Ignores any file beginning with . or #, or ending with ~
  574. * or .bak
  575. *
  576. *
  577. * @since jEdit 4.3pre5
  578. */
  579. public String[] _listDirectory(Object session, String directory,
  580. String glob, boolean recursive, Component comp,
  581. boolean skipBinary, boolean skipHidden)
  582. throws IOException
  583. {
  584. Log.log(Log.DEBUG,this,"Listing " + directory);
  585. ArrayList files = new ArrayList(100);
  586. Pattern filter;
  587. try
  588. {
  589. filter = Pattern.compile(MiscUtilities.globToRE(glob),
  590. Pattern.CASE_INSENSITIVE);
  591. }
  592. catch(PatternSyntaxException e)
  593. {
  594. Log.log(Log.ERROR,this,e);
  595. return null;
  596. }
  597. listFiles(session,new ArrayList(),files,directory,filter,
  598. recursive, comp, skipBinary, skipHidden);
  599. String[] retVal = (String[])files.toArray(new String[files.size()]);
  600. Arrays.sort(retVal,new MiscUtilities.StringICaseCompare());
  601. return retVal;
  602. } //}}}
  603. //{{{ _listFiles() method
  604. /**
  605. * Lists the specified directory.
  606. * @param session The session
  607. * @param directory The directory. Note that this must be a full
  608. * URL, including the host name, path name, and so on. The
  609. * username and password (if needed by the VFS) is obtained from the
  610. * session instance.
  611. * @param comp The component that will parent error dialog boxes
  612. * @exception IOException if an I/O error occurred
  613. * @since jEdit 4.3pre2
  614. */
  615. public VFSFile[] _listFiles(Object session, String directory,
  616. Component comp)
  617. throws IOException
  618. {
  619. return _listDirectory(session,directory,comp);
  620. } //}}}
  621. //{{{ _listDirectory() method
  622. /**
  623. * @deprecated Use <code>_listFiles()</code> instead.
  624. */
  625. public DirectoryEntry[] _listDirectory(Object session, String directory,
  626. Component comp)
  627. throws IOException
  628. {
  629. VFSManager.error(comp,directory,"vfs.not-supported.list",new String[] { name });
  630. return null;
  631. } //}}}
  632. //{{{ _getFile() method
  633. /**
  634. * Returns the specified directory entry.
  635. * @param session The session
  636. * @param path The path
  637. * @param comp The component that will parent error dialog boxes
  638. * @exception IOException if an I/O error occurred
  639. * @return The specified directory entry, or null if it doesn't exist.
  640. * @since jEdit 4.3pre2
  641. */
  642. public VFSFile _getFile(Object session, String path,
  643. Component comp)
  644. throws IOException
  645. {
  646. return _getDirectoryEntry(session,path,comp);
  647. } //}}}
  648. //{{{ _getDirectoryEntry() method
  649. /**
  650. * Returns the specified directory entry.
  651. * @param session The session
  652. * @param path The path
  653. * @param comp The component that will parent error dialog boxes
  654. * @exception IOException if an I/O error occurred
  655. * @return The specified directory entry, or null if it doesn't exist.
  656. * @since jEdit 2.7pre1
  657. */
  658. public DirectoryEntry _getDirectoryEntry(Object session, String path,
  659. Component comp)
  660. throws IOException
  661. {
  662. return null;
  663. } //}}}
  664. //{{{ DirectoryEntry class
  665. /**
  666. * @deprecated Use <code>VFSFile</code> instead.
  667. */
  668. public static class DirectoryEntry extends VFSFile
  669. {
  670. //{{{ DirectoryEntry constructor
  671. /**
  672. * @since jEdit 4.2pre2
  673. */
  674. public DirectoryEntry()
  675. {
  676. } //}}}
  677. //{{{ DirectoryEntry constructor
  678. public DirectoryEntry(String name, String path, String deletePath,
  679. int type, long length, boolean hidden)
  680. {
  681. this.name = name;
  682. this.path = path;
  683. this.deletePath = deletePath;
  684. this.symlinkPath = path;
  685. this.type = type;
  686. this.length = length;
  687. this.hidden = hidden;
  688. if(path != null)
  689. {
  690. // maintain backwards compatibility
  691. VFS vfs = VFSManager.getVFSForPath(path);
  692. canRead = ((vfs.getCapabilities() & READ_CAP) != 0);
  693. canWrite = ((vfs.getCapabilities() & WRITE_CAP) != 0);
  694. }
  695. } //}}}
  696. } //}}}
  697. //{{{ _delete() method
  698. /**
  699. * Deletes the specified URL.
  700. * @param session The VFS session
  701. * @param path The path
  702. * @param comp The component that will parent error dialog boxes
  703. * @exception IOException if an I/O error occurs
  704. * @since jEdit 2.7pre1
  705. */
  706. public boolean _delete(Object session, String path, Component comp)
  707. throws IOException
  708. {
  709. return false;
  710. } //}}}
  711. //{{{ _rename() method
  712. /**
  713. * Renames the specified URL. Some filesystems might support moving
  714. * URLs between directories, however others may not. Do not rely on
  715. * this behavior.
  716. * @param session The VFS session
  717. * @param from The old path
  718. * @param to The new path
  719. * @param comp The component that will parent error dialog boxes
  720. * @exception IOException if an I/O error occurs
  721. * @since jEdit 2.7pre1
  722. */
  723. public boolean _rename(Object session, String from, String to,
  724. Component comp) throws IOException
  725. {
  726. return false;
  727. } //}}}
  728. //{{{ _mkdir() method
  729. /**
  730. * Creates a new directory with the specified URL.
  731. * @param session The VFS session
  732. * @param directory The directory
  733. * @param comp The component that will parent error dialog boxes
  734. * @exception IOException if an I/O error occurs
  735. * @since jEdit 2.7pre1
  736. */
  737. public boolean _mkdir(Object session, String directory, Component comp)
  738. throws IOException
  739. {
  740. return false;
  741. } //}}}
  742. //{{{ _backup() method
  743. /**
  744. * Backs up the specified file. This should only be overriden by
  745. * the local filesystem VFS.
  746. * @param session The VFS session
  747. * @param path The path
  748. * @param comp The component that will parent error dialog boxes
  749. * @exception IOException if an I/O error occurs
  750. * @since jEdit 3.2pre2
  751. */
  752. public void _backup(Object session, String path, Component comp)
  753. throws IOException
  754. {
  755. } //}}}
  756. //{{{ _createInputStream() method
  757. /**
  758. * Creates an input stream. This method is called from the I/O
  759. * thread.
  760. * @param session the VFS session
  761. * @param path The path
  762. * @param ignoreErrors If true, file not found errors should be
  763. * ignored
  764. * @param comp The component that will parent error dialog boxes
  765. * @return an inputstream or <code>null</code> if there was a problem
  766. * @exception IOException If an I/O error occurs
  767. * @since jEdit 2.7pre1
  768. */
  769. public InputStream _createInputStream(Object session,
  770. String path, boolean ignoreErrors, Component comp)
  771. throws IOException
  772. {
  773. VFSManager.error(comp,path,"vfs.not-supported.load",new String[] { name });
  774. return null;
  775. } //}}}
  776. //{{{ _createOutputStream() method
  777. /**
  778. * Creates an output stream. This method is called from the I/O
  779. * thread.
  780. * @param session the VFS session
  781. * @param path The path
  782. * @param comp The component that will parent error dialog boxes
  783. * @exception IOException If an I/O error occurs
  784. * @since jEdit 2.7pre1
  785. */
  786. public OutputStream _createOutputStream(Object session,
  787. String path, Component comp)
  788. throws IOException
  789. {
  790. VFSManager.error(comp,path,"vfs.not-supported.save",new String[] { name });
  791. return null;
  792. } //}}}
  793. //{{{ _saveComplete() method
  794. /**
  795. * Called after a file has been saved.
  796. * @param session The VFS session
  797. * @param buffer The buffer
  798. * @param path The path the buffer was saved to (can be different from
  799. * {@link org.gjt.sp.jedit.Buffer#getPath()} if the user invoked the
  800. * <b>Save a Copy As</b> command, for example).
  801. * @param comp The component that will parent error dialog boxes
  802. * @exception IOException If an I/O error occurs
  803. * @since jEdit 4.1pre9
  804. */
  805. public void _saveComplete(Object session, Buffer buffer, String path,
  806. Component comp) throws IOException {} //}}}
  807. //{{{ _finishTwoStageSave() method
  808. /**
  809. * Called after a file has been saved and we use twoStageSave (first saving to
  810. * another file). This should re-apply permissions for example.
  811. * @param session The VFS session
  812. * @param buffer The buffer
  813. * @param path The path the buffer was saved to (can be different from
  814. * {@link org.gjt.sp.jedit.Buffer#getPath()} if the user invoked the
  815. * <b>Save a Copy As</b> command, for example).
  816. * @param comp The component that will parent error dialog boxes
  817. * @exception IOException If an I/O error occurs
  818. * @since jEdit 4.3pre4
  819. */
  820. public void _finishTwoStageSave(Object session, Buffer buffer, String path,
  821. Component comp) throws IOException
  822. {
  823. } //}}}
  824. //{{{ _endVFSSession() method
  825. /**
  826. * Finishes the specified VFS session. This must be called
  827. * after all I/O with this VFS is complete, to avoid leaving
  828. * stale network connections and such.
  829. * @param session The VFS session
  830. * @param comp The component that will parent error dialog boxes
  831. * @exception IOException if an I/O error occurred
  832. * @since jEdit 2.7pre1
  833. */
  834. public void _endVFSSession(Object session, Component comp)
  835. throws IOException
  836. {
  837. } //}}}
  838. //{{{ getDefaultColorFor() method
  839. /**
  840. * Returns color of the specified file name, by matching it against
  841. * user-specified regular expressions.
  842. * @since jEdit 4.0pre1
  843. */
  844. public static Color getDefaultColorFor(String name)
  845. {
  846. synchronized(lock)
  847. {
  848. if(colors == null)
  849. loadColors();
  850. for(int i = 0; i < colors.size(); i++)
  851. {
  852. ColorEntry entry = (ColorEntry)colors.elementAt(i);
  853. if(entry.re.matcher(name).matches())
  854. return entry.color;
  855. }
  856. return null;
  857. }
  858. } //}}}
  859. //{{{ DirectoryEntryCompare class
  860. /**
  861. * Implementation of {@link Comparator}
  862. * interface that compares {@link VFS.DirectoryEntry} instances.
  863. * @since jEdit 4.2pre1
  864. */
  865. public static class DirectoryEntryCompare implements Comparator
  866. {
  867. private boolean sortIgnoreCase, sortMixFilesAndDirs;
  868. /**
  869. * Creates a new <code>DirectoryEntryCompare</code>.
  870. * @param sortMixFilesAndDirs If false, directories are
  871. * put at the top of the listing.
  872. * @param sortIgnoreCase If false, upper case comes before
  873. * lower case.
  874. */
  875. public DirectoryEntryCompare(boolean sortMixFilesAndDirs,
  876. boolean sortIgnoreCase)
  877. {
  878. this.sortMixFilesAndDirs = sortMixFilesAndDirs;
  879. this.sortIgnoreCase = sortIgnoreCase;
  880. }
  881. public int compare(Object obj1, Object obj2)
  882. {
  883. VFSFile file1 = (VFSFile)obj1;
  884. VFSFile file2 = (VFSFile)obj2;
  885. if(!sortMixFilesAndDirs)
  886. {
  887. if(file1.getType() != file2.getType())
  888. return file2.getType() - file1.getType();
  889. }
  890. return MiscUtilities.compareStrings(file1.getName(),
  891. file2.getName(),sortIgnoreCase);
  892. }
  893. } //}}}
  894. //{{{ Private members
  895. private String name;
  896. private int caps;
  897. private String[] extAttrs;
  898. private static Vector colors;
  899. private static Object lock = new Object();
  900. //{{{ Class initializer
  901. static
  902. {
  903. EditBus.addToBus(new EBComponent()
  904. {
  905. public void handleMessage(EBMessage msg)
  906. {
  907. if(msg instanceof PropertiesChanged)
  908. {
  909. synchronized(lock)
  910. {
  911. colors = null;
  912. }
  913. }
  914. }
  915. });
  916. } //}}}
  917. //{{{ recursive listFiles() method
  918. private void listFiles(Object session, ArrayList stack,
  919. ArrayList files, String directory, Pattern glob, boolean recursive,
  920. Component comp, boolean skipBinary, boolean skipHidden) throws IOException
  921. {
  922. if(stack.contains(directory))
  923. {
  924. Log.log(Log.ERROR,this,
  925. "Recursion in _listDirectory(): "
  926. + directory);
  927. return;
  928. }
  929. else
  930. stack.add(directory);
  931. VFSFile[] _files = _listFiles(session,directory,
  932. comp);
  933. if(_files == null || _files.length == 0)
  934. return;
  935. for(int i = 0; i < _files.length; i++)
  936. {
  937. VFSFile file = _files[i];
  938. if (skipHidden && (file.isHidden() || MiscUtilities.isBackup(file.getName())))
  939. continue;
  940. if(file.getType() == VFSFile.DIRECTORY
  941. || file.getType() == VFSFile.FILESYSTEM)
  942. {
  943. if(recursive)
  944. {
  945. // resolve symlinks to avoid loops
  946. String canonPath = _canonPath(session,
  947. file.getPath(),comp);
  948. if(!MiscUtilities.isURL(canonPath))
  949. canonPath = MiscUtilities.resolveSymlinks(canonPath);
  950. listFiles(session,stack,files,
  951. canonPath,glob,recursive,
  952. comp, skipBinary, skipHidden);
  953. }
  954. }
  955. else // It's a regular file
  956. {
  957. if(!glob.matcher(file.getName()).matches())
  958. continue;
  959. if (skipBinary && file.isBinary(session))
  960. continue;
  961. Log.log(Log.DEBUG,this,file.getPath());
  962. files.add(file.getPath());
  963. }
  964. }
  965. } //}}}
  966. //{{{ loadColors() method
  967. private static void loadColors()
  968. {
  969. synchronized(lock)
  970. {
  971. colors = new Vector();
  972. if(!jEdit.getBooleanProperty("vfs.browser.colorize"))
  973. return;
  974. String glob;
  975. int i = 0;
  976. while((glob = jEdit.getProperty("vfs.browser.colors." + i + ".glob")) != null)
  977. {
  978. try
  979. {
  980. colors.addElement(new ColorEntry(
  981. Pattern.compile(MiscUtilities.globToRE(glob)),
  982. jEdit.getColorProperty(
  983. "vfs.browser.colors." + i + ".color",
  984. Color.black)));
  985. }
  986. catch(PatternSyntaxException e)
  987. {
  988. Log.log(Log.ERROR,VFS.class,"Invalid regular expression: "
  989. + glob);
  990. Log.log(Log.ERROR,VFS.class,e);
  991. }
  992. i++;
  993. }
  994. }
  995. } //}}}
  996. //{{{ ColorEntry class
  997. static class ColorEntry
  998. {
  999. Pattern re;
  1000. Color color;
  1001. ColorEntry(Pattern re, Color color)
  1002. {
  1003. this.re = re;
  1004. this.color = color;
  1005. }
  1006. } //}}}
  1007. //}}}
  1008. }