/jEdit/tags/jedit_43_with_gnuregexp_microstarxml/org/gjt/sp/jedit/io/VFSManager.java

# · Java · 478 lines · 257 code · 45 blank · 176 comment · 29 complexity · 49b29a073b2134e5b735d336fcce5cff MD5 · raw file

  1. /*
  2. * VFSManager.java - Main class of virtual filesystem
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2000, 2005 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 javax.swing.JOptionPane;
  25. import javax.swing.SwingUtilities;
  26. import java.awt.Component;
  27. import java.awt.Frame;
  28. import java.io.IOException;
  29. import java.util.ArrayList;
  30. import java.util.Collections;
  31. import java.util.Enumeration;
  32. import java.util.Hashtable;
  33. import java.util.LinkedList;
  34. import java.util.List;
  35. import java.util.Vector;
  36. import org.gjt.sp.jedit.gui.ErrorListDialog;
  37. import org.gjt.sp.jedit.msg.VFSUpdate;
  38. import org.gjt.sp.jedit.*;
  39. import org.gjt.sp.util.Log;
  40. import org.gjt.sp.util.WorkThreadPool;
  41. //}}}
  42. /**
  43. * jEdit's virtual filesystem allows it to transparently edit files
  44. * stored elsewhere than the local filesystem, for example on an FTP
  45. * site. See the {@link VFS} class for implementation details.<p>
  46. *
  47. * Note that most of the jEdit API is not thread-safe, so special care
  48. * must be taken when making jEdit API calls. Also, it is not safe to
  49. * call <code>SwingUtilities.invokeAndWait()</code> from a work request;
  50. * it can cause a deadlock if the given runnable then later calls
  51. * {@link #waitForRequests()}.
  52. *
  53. * @author Slava Pestov
  54. * @version $Id: VFSManager.java 5214 2005-04-21 20:32:29Z spestov $
  55. */
  56. public class VFSManager
  57. {
  58. /**
  59. * The service type. See {@link org.gjt.sp.jedit.ServiceManager}.
  60. * @since jEdit 4.2pre1
  61. */
  62. public static final String SERVICE = "org.gjt.sp.jedit.io.VFS";
  63. //{{{ init() method
  64. /**
  65. * Do not call.
  66. */
  67. public static void init()
  68. {
  69. int count = jEdit.getIntegerProperty("ioThreadCount",4);
  70. ioThreadPool = new WorkThreadPool("jEdit I/O",count);
  71. JARClassLoader classLoader = new JARClassLoader();
  72. for(int i = 0; i < ioThreadPool.getThreadCount(); i++)
  73. {
  74. ioThreadPool.getThread(i).setContextClassLoader(
  75. classLoader);
  76. }
  77. } //}}}
  78. //{{{ start() method
  79. /**
  80. * Do not call.
  81. */
  82. public static void start()
  83. {
  84. ioThreadPool.start();
  85. } //}}}
  86. //{{{ VFS methods
  87. //{{{ getFileVFS() method
  88. /**
  89. * Returns the local filesystem VFS.
  90. * @since jEdit 2.5pre1
  91. */
  92. public static VFS getFileVFS()
  93. {
  94. return fileVFS;
  95. } //}}}
  96. //{{{ getUrlVFS() method
  97. /**
  98. * Returns the URL VFS.
  99. * @since jEdit 2.5pre1
  100. */
  101. public static VFS getUrlVFS()
  102. {
  103. return urlVFS;
  104. } //}}}
  105. //{{{ getVFSByName() method
  106. /**
  107. * @deprecated Use <code>getVFSForProtocol()</code> instead.
  108. */
  109. public static VFS getVFSByName(String name)
  110. {
  111. // in new api, protocol always equals name
  112. VFS vfs = (VFS)ServiceManager.getService(SERVICE,name);
  113. if(vfs == null)
  114. return (VFS)vfsHash.get(name);
  115. else
  116. return vfs;
  117. } //}}}
  118. //{{{ getVFSForProtocol() method
  119. /**
  120. * Returns the VFS for the specified protocol.
  121. * @param protocol The protocol
  122. * @since jEdit 2.5pre1
  123. */
  124. public static VFS getVFSForProtocol(String protocol)
  125. {
  126. if(protocol.equals("file"))
  127. return fileVFS;
  128. else
  129. {
  130. VFS vfs = (VFS)ServiceManager.getService(SERVICE,protocol);
  131. if(vfs == null)
  132. vfs = (VFS)protocolHash.get(protocol);
  133. if(vfs != null)
  134. return vfs;
  135. else
  136. return urlVFS;
  137. }
  138. } //}}}
  139. //{{{ getVFSForPath() method
  140. /**
  141. * Returns the VFS for the specified path.
  142. * @param path The path
  143. * @since jEdit 2.6pre4
  144. */
  145. public static VFS getVFSForPath(String path)
  146. {
  147. if(MiscUtilities.isURL(path))
  148. return getVFSForProtocol(MiscUtilities.getProtocolOfURL(path));
  149. else
  150. return fileVFS;
  151. } //}}}
  152. //{{{ registerVFS() method
  153. /**
  154. * @deprecated Write a <code>services.xml</code> file instead;
  155. * see {@link org.gjt.sp.jedit.ServiceManager}.
  156. */
  157. public static void registerVFS(String protocol, VFS vfs)
  158. {
  159. Log.log(Log.DEBUG,VFSManager.class,"Registered "
  160. + vfs.getName() + " filesystem for "
  161. + protocol + " protocol");
  162. vfsHash.put(vfs.getName(),vfs);
  163. protocolHash.put(protocol,vfs);
  164. } //}}}
  165. //{{{ getFilesystems() method
  166. /**
  167. * @deprecated Use <code>getVFSs()</code> instead.
  168. */
  169. public static Enumeration getFilesystems()
  170. {
  171. return vfsHash.elements();
  172. } //}}}
  173. //{{{ getVFSs() method
  174. /**
  175. * Returns a list of all registered filesystems.
  176. * @since jEdit 4.2pre1
  177. */
  178. public static String[] getVFSs()
  179. {
  180. // the sooner ppl move to the new api, the less we'll need
  181. // crap like this
  182. List returnValue = new LinkedList();
  183. String[] newAPI = ServiceManager.getServiceNames(SERVICE);
  184. if(newAPI != null)
  185. {
  186. for(int i = 0; i < newAPI.length; i++)
  187. {
  188. returnValue.add(newAPI[i]);
  189. }
  190. }
  191. Enumeration oldAPI = vfsHash.keys();
  192. while(oldAPI.hasMoreElements())
  193. returnValue.add(oldAPI.nextElement());
  194. return (String[])returnValue.toArray(new String[
  195. returnValue.size()]);
  196. } //}}}
  197. //}}}
  198. //{{{ I/O request methods
  199. //{{{ getIOThreadPool() method
  200. /**
  201. * Returns the I/O thread pool.
  202. */
  203. public static WorkThreadPool getIOThreadPool()
  204. {
  205. return ioThreadPool;
  206. } //}}}
  207. //{{{ waitForRequests() method
  208. /**
  209. * Returns when all pending requests are complete.
  210. * @since jEdit 2.5pre1
  211. */
  212. public static void waitForRequests()
  213. {
  214. ioThreadPool.waitForRequests();
  215. } //}}}
  216. //{{{ errorOccurred() method
  217. /**
  218. * Returns if the last request caused an error.
  219. */
  220. public static boolean errorOccurred()
  221. {
  222. return error;
  223. } //}}}
  224. //{{{ getRequestCount() method
  225. /**
  226. * Returns the number of pending I/O requests.
  227. */
  228. public static int getRequestCount()
  229. {
  230. return ioThreadPool.getRequestCount();
  231. } //}}}
  232. //{{{ runInAWTThread() method
  233. /**
  234. * Executes the specified runnable in the AWT thread once all
  235. * pending I/O requests are complete.
  236. * @since jEdit 2.5pre1
  237. */
  238. public static void runInAWTThread(Runnable run)
  239. {
  240. ioThreadPool.addWorkRequest(run,true);
  241. } //}}}
  242. //{{{ runInWorkThread() method
  243. /**
  244. * Executes the specified runnable in one of the I/O threads.
  245. * @since jEdit 2.6pre2
  246. */
  247. public static void runInWorkThread(Runnable run)
  248. {
  249. ioThreadPool.addWorkRequest(run,false);
  250. } //}}}
  251. //}}}
  252. //{{{ error() method
  253. /**
  254. * Handle an I/O error.
  255. * @since jEdit 4.3pre3
  256. */
  257. public static void error(IOException e, String path, Component comp)
  258. {
  259. Log.log(Log.ERROR,VFSManager.class,e);
  260. VFSManager.error(comp,path,"ioerror",new String[] { e.toString() });
  261. } //}}}
  262. //{{{ error() method
  263. /**
  264. * @deprecated Call the other <code>error()</code> method instead.
  265. */
  266. public static void error(final Component comp, final String error, final Object[] args)
  267. {
  268. // if we are already in the AWT thread, take a shortcut
  269. if(SwingUtilities.isEventDispatchThread())
  270. {
  271. GUIUtilities.error(comp,error,args);
  272. return;
  273. }
  274. // the 'error' chicanery ensures that stuff like:
  275. // VFSManager.waitForRequests()
  276. // if(VFSManager.errorOccurred())
  277. // ...
  278. // will work (because the below runnable will only be
  279. // executed in the next event)
  280. VFSManager.error = true;
  281. runInAWTThread(new Runnable()
  282. {
  283. public void run()
  284. {
  285. VFSManager.error = false;
  286. if(comp == null || !comp.isShowing())
  287. GUIUtilities.error(null,error,args);
  288. else
  289. GUIUtilities.error(comp,error,args);
  290. }
  291. });
  292. } //}}}
  293. //{{{ error() method
  294. /**
  295. * Reports an I/O error.
  296. *
  297. * @param comp The component
  298. * @param path The path name that caused the error
  299. * @param messageProp The error message property name
  300. * @param args Positional parameters
  301. * @since jEdit 4.0pre3
  302. */
  303. public static void error(Component comp,
  304. final String path,
  305. String messageProp,
  306. Object[] args)
  307. {
  308. final Frame frame = JOptionPane.getFrameForComponent(comp);
  309. synchronized(errorLock)
  310. {
  311. error = true;
  312. errors.addElement(new ErrorListDialog.ErrorEntry(
  313. path,messageProp,args));
  314. if(errors.size() == 1)
  315. {
  316. VFSManager.runInAWTThread(new Runnable()
  317. {
  318. public void run()
  319. {
  320. String caption = jEdit.getProperty(
  321. "ioerror.caption" + (errors.size() == 1
  322. ? "-1" : ""),new Integer[] {
  323. new Integer(errors.size()) });
  324. new ErrorListDialog(
  325. frame.isShowing()
  326. ? frame
  327. : jEdit.getFirstView(),
  328. jEdit.getProperty("ioerror.title"),
  329. caption,errors,false);
  330. errors.removeAllElements();
  331. error = false;
  332. }
  333. });
  334. }
  335. }
  336. } //}}}
  337. //{{{ sendVFSUpdate() method
  338. /**
  339. * Sends a VFS update message.
  340. * @param vfs The VFS
  341. * @param path The path that changed
  342. * @param parent True if an update should be sent for the path's
  343. * parent too
  344. * @since jEdit 2.6pre4
  345. */
  346. public static void sendVFSUpdate(VFS vfs, String path, boolean parent)
  347. {
  348. if(parent)
  349. {
  350. sendVFSUpdate(vfs,vfs.getParentOfPath(path),false);
  351. sendVFSUpdate(vfs,path,false);
  352. }
  353. else
  354. {
  355. // have to do this hack until VFSPath class is written
  356. if(path.length() != 1 && (path.endsWith("/")
  357. || path.endsWith(java.io.File.separator)))
  358. path = path.substring(0,path.length() - 1);
  359. synchronized(vfsUpdateLock)
  360. {
  361. for(int i = 0; i < vfsUpdates.size(); i++)
  362. {
  363. VFSUpdate msg = (VFSUpdate)vfsUpdates
  364. .get(i);
  365. if(msg.getPath().equals(path))
  366. {
  367. // don't send two updates
  368. // for the same path
  369. return;
  370. }
  371. }
  372. vfsUpdates.add(new VFSUpdate(path));
  373. if(vfsUpdates.size() == 1)
  374. {
  375. // we were the first to add an update;
  376. // add update sending runnable to AWT
  377. // thread
  378. VFSManager.runInAWTThread(new SendVFSUpdatesSafely());
  379. }
  380. }
  381. }
  382. } //}}}
  383. //{{{ SendVFSUpdatesSafely class
  384. static class SendVFSUpdatesSafely implements Runnable
  385. {
  386. public void run()
  387. {
  388. synchronized(vfsUpdateLock)
  389. {
  390. // the vfs browser has what you might call
  391. // a design flaw, it doesn't update properly
  392. // unless the vfs update for a parent arrives
  393. // before any updates for the children. sorting
  394. // the list alphanumerically guarantees this.
  395. Collections.sort(vfsUpdates,
  396. new MiscUtilities.StringCompare()
  397. );
  398. for(int i = 0; i < vfsUpdates.size(); i++)
  399. {
  400. EditBus.send((VFSUpdate)vfsUpdates.get(i));
  401. }
  402. vfsUpdates.clear();
  403. }
  404. }
  405. } //}}}
  406. //{{{ Private members
  407. //{{{ Static variables
  408. private static WorkThreadPool ioThreadPool;
  409. private static VFS fileVFS;
  410. private static VFS urlVFS;
  411. private static Hashtable vfsHash;
  412. private static Hashtable protocolHash;
  413. private static boolean error;
  414. private static Object errorLock;
  415. private static Vector errors;
  416. private static Object vfsUpdateLock;
  417. private static List vfsUpdates;
  418. //}}}
  419. //{{{ Class initializer
  420. static
  421. {
  422. errorLock = new Object();
  423. errors = new Vector();
  424. fileVFS = new FileVFS();
  425. urlVFS = new UrlVFS();
  426. vfsHash = new Hashtable();
  427. protocolHash = new Hashtable();
  428. vfsUpdateLock = new Object();
  429. vfsUpdates = new ArrayList(10);
  430. } //}}}
  431. private VFSManager() {}
  432. //}}}
  433. }