PageRenderTime 34ms CodeModel.GetById 20ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

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