Filesystems API

Overview

# Javadoc

Take a look at the Javadoc, especially for `FileObject`.

# Contents

- What This API is Not For
- Common Tasks
  - Finding files and folders
  - Creating, deleting, and renaming files and folders
  - Reading and writing files
  - Listening on file events
  - MIME Type Resolution Process
- Special Filesystems
- `FileObject` UML class diagram

# Filesystems API

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.

## What This API is Not For

This API pertains only to manipulating files on disk (or whatever storage mechanism a filesystem may use), and makes no reference to their contents nor to how they are being used elsewhere in the IDE (beyond whether or not they are locked). If you are looking for information on how to create custom content types, use the editor, create Explorer nodes corresponding to files, etc., this API is not the primary document you should be reading.

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().

## Common Tasks

Basic operations using the Filesystems API should be familiar, as they are similar to any file-access system. `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.

### Finding files and folders

Normally, you will be looking for a file or folder by name, and will want to get the `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);

### Creating, deleting, and renaming files and folders

This example creates a subfolder and then a new file within that subfolder:

    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.

### Reading and writing files

Reading and writing the contents of a data file is straightforward:

    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();
    }

### Listening on file events

If you need to keep track of what is being done to a file by other components, you can monitor it using normal Java events:

    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.");
        }
    });

### Determining MIME Content Type

`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.

## Special Filesystems

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.

## UML class diagram of `FileObject`

@FOOTER@