PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/io/VFSManager.java

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