📄 vfs.java
字号:
/* * VFS.java - Virtual filesystem implementation * :tabSize=8:indentSize=8:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2000, 2003 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */package org.gjt.sp.jedit.io;//{{{ Importsimport gnu.regexp.*;import java.awt.Color;import java.awt.Component;import java.io.*;import java.util.*;import org.gjt.sp.jedit.buffer.BufferIORequest;import org.gjt.sp.jedit.msg.PropertiesChanged;import org.gjt.sp.jedit.*;import org.gjt.sp.util.Log;//}}}/** * A virtual filesystem implementation.<p> * * Plugins can provide virtual file systems by defining entries in their * <code>services.xml</code> files like so: * * <pre><SERVICE CLASS="org.gjt.sp.jedit.io.VFS" NAME="<i>name</i>"> * new <i>MyVFS</i>(); *</SERVICE></pre> * * URLs of the form <code><i>name</i>:<i>path</i></code> will then be handled * by the VFS named <code><i>name</i></code>.<p> * * See {@link org.gjt.sp.jedit.ServiceManager} for details.<p> * * <h3>Session objects:</h3> * * A session is used to persist things like login information, any network * sockets, etc. File system implementations that do not need this kind of * persistence return a dummy object as a session.<p> * * Methods whose names are prefixed with "_" expect to be given a * previously-obtained session object. A session must be obtained from the AWT * thread in one of two ways: * * <ul> * <li>{@link #createVFSSession(String,Component)}</li> * <li>{@link #showBrowseDialog(Object[],Component)}</li> * </ul> * * When done, the session must be disposed of using * {@link #_endVFSSession(Object,Component)}.<p> * * <h3>Thread safety:</h3> * * The following methods cannot be called from an I/O thread: * * <ul> * <li>{@link #createVFSSession(String,Component)}</li> * <li>{@link #insert(View,Buffer,String)}</li> * <li>{@link #load(View,Buffer,String)}</li> * <li>{@link #save(View,Buffer,String)}</li> * <li>{@link #showBrowseDialog(Object[],Component)}</li> * </ul> * * All remaining methods are required to be thread-safe in subclasses. * * <h3>Implementing a VFS</h3> * * You can override as many or as few methods as you want. Make sure * {@link #getCapabilities()} returns a value reflecting the functionality * implemented by your VFS. * * @see VFSManager#getVFSForPath(String) * @see VFSManager#getVFSForProtocol(String) * * @author Slava Pestov * @author $Id: VFS.java,v 1.39 2003/09/08 01:24:11 spestov Exp $ */public abstract class VFS{ //{{{ Capabilities /** * Read capability. * @since jEdit 2.6pre2 */ public static final int READ_CAP = 1 << 0; /** * Write capability. * @since jEdit 2.6pre2 */ public static final int WRITE_CAP = 1 << 1; /** * @deprecated Do not define this capability.<p> * * This was the official API for adding items to a file * system browser's <b>Plugins</b> menu in jEdit 4.1 and earlier. In * jEdit 4.2, there is a different way of doing this, you must provide * a <code>browser.actions.xml</code> file in your plugin JAR, and * define <code>plugin.<i>class</i>.browser-menu-item</code> * or <code>plugin.<i>class</i>.browser-menu</code> properties. * See {@link org.gjt.sp.jedit.EditPlugin} for details. */ public static final int BROWSE_CAP = 1 << 2; /** * Delete file capability. * @since jEdit 2.6pre2 */ public static final int DELETE_CAP = 1 << 3; /** * Rename file capability. * @since jEdit 2.6pre2 */ public static final int RENAME_CAP = 1 << 4; /** * Make directory capability. * @since jEdit 2.6pre2 */ public static final int MKDIR_CAP = 1 << 5; /** * Low latency capability. If this is not set, then a confirm dialog * will be shown before doing a directory search in this VFS. * @since jEdit 4.1pre1 */ public static final int LOW_LATENCY_CAP = 1 << 6; /** * Case insensitive file system capability. * @since jEdit 4.1pre1 */ public static final int CASE_INSENSITIVE_CAP = 1 << 7; //}}} //{{{ Extended attributes /** * File type. * @since jEdit 4.2pre1 */ public static final String EA_TYPE = "type"; /** * File status (read only, read write, etc). * @since jEdit 4.2pre1 */ public static final String EA_STATUS = "status"; /** * File size. * @since jEdit 4.2pre1 */ public static final String EA_SIZE = "size"; /** * File last modified date. * @since jEdit 4.2pre1 */ public static final String EA_MODIFIED = "modified"; //}}} //{{{ VFS constructor /** * @deprecated Use the form where the constructor takes a capability * list. */ public VFS(String name) { this(name,0); } //}}} //{{{ VFS constructor /** * Creates a new virtual filesystem. * @param name The name * @param caps The capabilities */ public VFS(String name, int caps) { this.name = name; this.caps = caps; // reasonable defaults (?) this.extAttrs = new String[] { EA_SIZE, EA_TYPE }; } //}}} //{{{ VFS constructor /** * Creates a new virtual filesystem. * @param name The name * @param caps The capabilities * @param extAttrs The extended attributes * @since jEdit 4.2pre1 */ public VFS(String name, int caps, String[] extAttrs) { this.name = name; this.caps = caps; this.extAttrs = extAttrs; } //}}} //{{{ getName() method /** * Returns this VFS's name. The name is used to obtain the * label stored in the <code>vfs.<i>name</i>.label</code> * property. */ public String getName() { return name; } //}}} //{{{ getCapabilities() method /** * Returns the capabilities of this VFS. * @since jEdit 2.6pre2 */ public int getCapabilities() { return caps; } //}}} //{{{ getExtendedAttributes() method /** * Returns the extended attributes supported by this VFS. * @since jEdit 4.2pre1 */ public String[] getExtendedAttributes() { return extAttrs; } //}}} //{{{ showBrowseDialog() method /** * Displays a dialog box that should set up a session and return * the initial URL to browse. * @param session Where the VFS session will be stored * @param comp The component that will parent error dialog boxes * @return The URL * @since jEdit 2.7pre1 */ public String showBrowseDialog(Object[] session, Component comp) { return null; } //}}} //{{{ getFileName() method /** * Returns the file name component of the specified path. * @param path The path * @since jEdit 3.1pre4 */ public String getFileName(String path) { if(path.equals("/")) return path; if(path.endsWith("/") || path.endsWith(File.separator)) path = path.substring(0,path.length() - 1); int index = Math.max(path.lastIndexOf('/'), path.lastIndexOf(File.separatorChar)); if(index == -1) index = path.indexOf(':'); // don't want getFileName("roots:") to return "" if(index == -1 || index == path.length() - 1) return path; return path.substring(index + 1); } //}}} //{{{ getParentOfPath() method /** * Returns the parent of the specified path. This must be * overridden to return a non-null value for browsing of this * filesystem to work. * @param path The path * @since jEdit 2.6pre5 */ public String getParentOfPath(String path) { // ignore last character of path to properly handle // paths like /foo/bar/ int count = Math.max(0,path.length() - 2); int index = path.lastIndexOf(File.separatorChar,count); if(index == -1) index = path.lastIndexOf('/',count); if(index == -1) { // this ensures that getFileParent("protocol:"), for // example, is "protocol:" and not "". index = path.lastIndexOf(':'); } return path.substring(0,index + 1); } //}}} //{{{ constructPath() method /** * Constructs a path from the specified directory and * file name component. This must be overridden to return a * non-null value, otherwise browsing this filesystem will * not work.<p> * * Unless you are writing a VFS, this method should not be called * directly. To ensure correct behavior, you <b>must</b> call * {@link org.gjt.sp.jedit.MiscUtilities#constructPath(String,String)} * instead. * * @param parent The parent directory * @param path The path * @since jEdit 2.6pre2 */ public String constructPath(String parent, String path) { return parent + path; } //}}} //{{{ getFileSeparator() method /** * Returns the file separator used by this VFS. * @since jEdit 2.6pre9 */ public char getFileSeparator() { return '/'; } //}}} //{{{ getTwoStageSaveName() method /** * Returns a temporary file name based on the given path. * * By default jEdit first saves a file to <code>#<i>name</i>#save#</code> * and then renames it to the original file. However some virtual file * systems might not support the <code>#</code> character in filenames, * so this method permits the VFS to override this behavior. * * @param path The path name * @since jEdit 4.1pre7 */ public String getTwoStageSaveName(String path) { return MiscUtilities.constructPath(getParentOfPath(path), '#' + getFileName(path) + "#save#"); } //}}} //{{{ reloadDirectory() method /** * Called before a directory is reloaded by the file system browser. * Can be used to flush a cache, etc. * @since jEdit 4.0pre3 */ public void reloadDirectory(String path) {} //}}} //{{{ createVFSSession() method /** * Creates a VFS session. This method is called from the AWT thread, * so it should not do any I/O. It could, however, prompt for * a login name and password, for example. * @param path The path in question * @param comp The component that will parent any dialog boxes shown * @return The session * @since jEdit 2.6pre3 */ public Object createVFSSession(String path, Component comp) { return new Object(); } //}}} //{{{ load() method /** * Loads the specified buffer. The default implementation posts * an I/O request to the I/O thread. * @param view The view * @param buffer The buffer * @param path The path */ public boolean load(View view, Buffer buffer, String path) { if((getCapabilities() & READ_CAP) == 0) { VFSManager.error(view,path,"vfs.not-supported.load",new String[] { name }); return false; } Object session = createVFSSession(path,view); if(session == null) return false; if((getCapabilities() & WRITE_CAP) == 0) buffer.setReadOnly(true); BufferIORequest request = new BufferIORequest( BufferIORequest.LOAD,view,buffer,session,this,path); if(buffer.isTemporary()) // this makes HyperSearch much faster request.run(); else VFSManager.runInWorkThread(request); return true; } //}}} //{{{ save() method /** * Saves the specifies buffer. The default implementation posts * an I/O request to the I/O thread. * @param view The view * @param buffer The buffer * @param path The path */ public boolean save(View view, Buffer buffer, String path) { if((getCapabilities() & WRITE_CAP) == 0) { VFSManager.error(view,path,"vfs.not-supported.save",new String[] { name }); return false; } Object session = createVFSSession(path,view); if(session == null) return false; /* When doing a 'save as', the path to save to (path) * will not be the same as the buffer's previous path * (buffer.getPath()). In that case, we want to create * a backup of the new path, even if the old path was * backed up as well (BACKED_UP property set) */ if(!path.equals(buffer.getPath())) buffer.unsetProperty(Buffer.BACKED_UP); VFSManager.runInWorkThread(new BufferIORequest( BufferIORequest.SAVE,view,buffer,session,this,path)); return true; } //}}} //{{{ insert() method /** * Inserts a file into the specified buffer. The default implementation * posts an I/O request to the I/O thread. * @param view The view * @param buffer The buffer * @param path The path */ public boolean insert(View view, Buffer buffer, String path) { if((getCapabilities() & READ_CAP) == 0) { VFSManager.error(view,path,"vfs.not-supported.load",new String[] { name }); return false; } Object session = createVFSSession(path,view); if(session == null) return false; VFSManager.runInWorkThread(new BufferIORequest( BufferIORequest.INSERT,view,buffer,session,this,path)); return true; } //}}} // A method name that starts with _ requires a session object //{{{ _canonPath() method /** * Returns the canonical form of the specified path name. For example, * <code>~</code> might be expanded to the user's home directory. * @param session The session * @param path The path * @param comp The component that will parent error dialog boxes * @exception IOException if an I/O error occurred * @since jEdit 4.0pre2 */ public String _canonPath(Object session, String path, Component comp) throws IOException { return path; } //}}} //{{{ _listDirectory() method /** * A convinience method that matches file names against globs, and can * optionally list the directory recursively. * @param session The session * @param directory The directory. Note that this must be a full * URL, including the host name, path name, and so on. The * username and password (if needed by the VFS) is obtained from the * session instance. * @param glob Only file names matching this glob will be returned * @param recursive If true, subdirectories will also be listed. * @param comp The component that will parent error dialog boxes * @exception IOException if an I/O error occurred * @since jEdit 4.1pre1 */ public String[] _listDirectory(Object session, String directory, String glob, boolean recursive, Component comp)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -