FileObject
.
The Filesystems API permits module authors to access files in a uniform manner: e.g. you may be unaware of whether a "file object" you are using is a plain disk file, or a JAR entry.
A FileSystem is a namespace for FileObjects - a FileSystem has a root folder, which is a FileObject, which may have child files and folders - just like a file system on disk. A FileSystem may or may not represent actual files on disk; this API can be used to represent any hierarchical storage of file-like data.
From the perspective of the Filesystems API, all files consist of byte streams (albeit with MIME types). Usually, the functionality of this API will actually be used indirectly, via the Loaders API, which abstracts away most of the details of files and presents data in terms of DataObjects, which typically represent the parsed content of a file and provide objects to programmatically access that content via DataObject.getCookie().
FileObjects
for a user's data on disk are typically a thin wrapper around
java.io.File
; elements of NetBeans' internal configuration
are also FileObjects
, these typically representing data
actually stored in an XML file inside a module's jar.
There are a few differences from traditional file-access APIs, such as monitoring files/folders for changes. This section gives examples of the more common tasks associated with using filesystems.
FileObject
which represents it.
If you need to get a file object corresponding to a file on disk, use:
fileObject = FileUtil.toFileObject(new File("/some/path/to/file.txt"));
FileUtil
also has methods for working with archive (ZIP/JAR) entries. File objects corresponding
to archive entries are read-only but otherwise behave much like disk files.
To find all the folders and files directly contained in this folder, you may use:
FileObject children[]=folder.getChildren();Occasionally you may need to present a given file object as a URL; for example, to display it in a web browser. This is straightforward:
URL url = file.getURL(); HtmlBrowser.URLDisplayer.getDefault().showURL(url);
FileObject subfolder=folder.createFolder("sub"); FileObject newfile=subfolder.createData("NewSource", "java");
You can delete a file easily:
newfile.delete();If you want to rename a file, you must first take out a lock on the file, to make sure that no one else is actively using the file at the same time. Then you may rename it:
FileLock lock = null; try { lock=newfile.lock(); } catch (FileAlreadyLockedException e) { // Try again later; perhaps display a warning dialog. return; } try { newfile.rename(lock, "NewSrc", "java"); } finally { // Always put this in a finally block! lock.releaseLock(); }If you want to move a file into a different directory (or even file system), you cannot use
rename(...)
; the easiest way is
to use a NetBeans helper method:
FileObject someFile; FileObject whereTo; FileUtil.moveFile(someFile, whereTo, "YourSource");Note that in the current API set, it is neither possible nor necessary to lock folders (e.g. when creating new children), as normally locks are used to protect data files from conflicts between the Editor, the Explorer, and so on. If in the future there are thread-related problems associated with improper simultaneous access to the same folder, support for folder locking could be added to the Filesystems API.
Similarly, there is no support currently for nonexclusive read locks - if you require exclusion of writers during a read, you must take out a regular write lock for the duration of the read. This is not normally necessary, since typically only the Editor will be reading and writing the contents of the file, and other file operations do not involve information which could be partially corrupted between threads. If necessary, the API includes facilities for read-many/write-one locks.
BufferedReader from=new BufferedReader(new InputStreamReader(someFile.getInputStream())); try { String line; while ((line=from.readLine()) != null) { // do something with line } finally { from.close(); } FileLock lock; try { lock=someFile.lock(); } catch (FileAlreadyLockedException e) { return; } try { PrintWriter to=new PrintWriter(someFile.getOutputStream(lock)); try { to.println("testing..."); to.println("1..2..3.."); } finally { to.close(); } } finally { lock.releaseLock(); }
someFile.addFileChangeListener(new FileChangeAdapter() { public void fileChanged(FileEvent ev) { System.out.println("Contents changed."); } public void fileAttributeChanged(FileAttributeEvent ev) { System.out.println(ev.getName() + ": " + ev.getOldValue() + " -> " + ev.getNewValue()); } });All events affecting existing files are actually fired twice, once from the file itself and once from its containing folder, so you may just want to listen on the parent folder. Also, file creation events are fired on the folder only, of course:
FileObject someFolder=someFile.getParent(); someFolder.addFileChangeListener(new FileChangeAdapter() { public void fileChanged(FileEvent ev) { System.out.println("Contents of " + ev.getFile() + " changed."); } public void fileDataCreated(FileEvent ev) { System.out.println("File " + ev.getFile() + " created."); } });
FileObject.getMIMEType()
reports a basic MIME type for a file, which can used to classify it for
editing and other purposes.
If you need to influence the MIME type resolution process, you can register a MIMEResolver
.
To simplify this process you can register a MIMEResolver
declaratively.
It is not only easier (no coding needed) but can be more efficient by
sharing results among multiple declared resolvers.
See the declarative MIME resolvers how-to for more information about this.
Each file object resides on a FileSystem
which handles a large tree of files. Normally you need
not be aware of this, since the "Master Filesystem" module automatically handles creation of whatever
FileSystem
s are needed to represent any files on disk, as well as any ZIP/JAR entries for archives
on disk (using JarFileSystem
). It is possible to implement your own filesystem for specialized
purposes (normally by subclassing AbstractFileSystem
and perhaps registering a corresponding
URLMapper
).
The Filesystems API also includes a couple of special implementations which are used by the core of the IDE
to assemble the system filesystem used for application configuration, and could also be used by module writers
in some circumstances: MultiFileSystem
and XMLFileSystem
. See their Javadoc for more
information.