PageRenderTime 95ms CodeModel.GetById 67ms app.highlight 23ms RepoModel.GetById 0ms app.codeStats 1ms

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

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