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

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