Class MultiFileSystem

java.lang.Object
org.openide.filesystems.FileSystem
org.openide.filesystems.MultiFileSystem
All Implemented Interfaces:
Serializable

public class MultiFileSystem extends FileSystem
General base class for filesystems which proxy to others.

This filesystem has no form of storage in and of itself. Rather, it composes and proxies one or more "delegate" filesystems. The result is that of a "layered" sandwich of filesystems, each able to provide files to appear in the merged result. Often the frontmost layer will be writable, and all changes to the filesystem are sent to this layer, but that behavior is configurable.

The layers are ordered so that entries in a filesystem in "front" can override one in "back". Since it is often not straightforward to arrange layers so that particular overrides work, as of org.openide.filesystems 7.36 you may set the special file attribute weight to any Number on a layer entry. (If unspecified, the implicit default value is zero.) A variant with a higher weight will override one with a lower weight even if it is further back. (The exception is that entries in a writable frontmost layer always override other layers, regardless of weight.)

Creating a new MultiFileSystem is easy in the simplest cases: just call MultiFileSystem(FileSystem[]) and pass a list of delegates. If you pass it only read-only delegates, the composite will also be read-only. Or you may pass it one or more writable filesystems (make sure the first file system is one of them); then it will be able to act as a writable file system, by default storing all actual changes on the first file system.

This class is intended to be subclassed more than to be used as is, since typically a user of it will want finer-grain control over several aspects of its operation. When subclassed, delegates may be changed dynamically and the "contents" of the composite filesystem will automatically be updated.

One of the more common methods to override is createWritableOn(java.lang.String), which should provide the delegate filesystem which should be used to store changes to the indicated file (represented as a resource path). Normally, this is hardcoded to provide the first filesystem (assuming it is writable); subclasses may store changes on another filesystem, or they may select a filesystem to store changes on according to (for example) file extension. This could be used to separate source from compiled/deployable files, for instance. Or some filesystems may prefer to attempt to make changes in whatever filesystem provided the original file, assuming it is writable; this is also possible, getting information about the file's origin from findSystem(org.openide.filesystems.FileObject).

Another likely candidate for overriding is findResourceOn(org.openide.filesystems.FileSystem, java.lang.String). Normally this method just looks up resources by the normal means on delegate filesystems and provides the resultant file objects. However it could be customized to "warp" the delegates somehow; for example, selecting a different virtual root for one of them.

notifyMigration(org.openide.filesystems.FileObject) provides a means for subclasses to update some state (for example, visual annotations) when the filesystem used to produce a given file changes dynamically. This most often happens when a file provided by a back delegate is written to, and the result stored on the foremost delegate, but it could occur in other situations too (e.g. change of delegates, removal of overriding file on underlying front delegate, etc.).

When new files are added to the MultiFileSystem, normally they will be physically added to the foremost delegate, although as previously mentioned they can be customized. When their contents (or attributes) are changed, by default these changes are again made in the foremost delegate. But what if a file is deleted? There must be a way to indicate on the foremost delegate that it should not appear in the composite (even while it remains in one of the delegates). For this reason, MultiFileSystem uses "masks" which are simply empty files named according to the file they should hide, but with the suffix _hidden. Thus, for example, if there is a file subdir/readme.txt_hidden in a front delegate it will hide any files named subdir/readme.txt in delegates further back. These masks are automatically created as needed when files are "deleted" from MultiFileSystem; or delegate filesystems may explicitly provide them. Normally the mask files are not themselves visible as FileObjects, since they are an artifact of the deletion logic. However, when nesting MultiFileSystems inside other MultiFileSystems, it can be useful to have delegates be able to mask files not provided by their immediate siblings, but by cousins. For this reason, nested subclasses may call setPropagateMasks(boolean) to make the mask files propagate up one or more nesting levels and thus remain potent against cousin delegates.

To support rollback, two pseudo-attribute is defined since 8.5: revealEntries typed as Map<String, FileObject & Callable>. The attribute is available on a folder, and contains information on child FileObjects, which have been overriden or masked by the writable layer of the MFS. Map is keyed by child name, the value is a FileObject that allows access to the masked child attributes and/or content.

The returned FileObjects do not leak its neighbours from the lower layers. The parent, children or siblings are returned from the MFS, if they exist. The FileObjects can be also casted to Callable<FileObject>. When called, the original version of the file is restored on the MultiFileSystem, and the restored instance is returned as result.

See Also:
  • Constructor Details

    • MultiFileSystem

      protected MultiFileSystem()
      Creates new empty MultiFileSystem. Useful only for subclasses.
    • MultiFileSystem

      public MultiFileSystem(FileSystem... fileSystems)
      Creates new MultiFileSystem.
      Parameters:
      fileSystems - array of filesystems (can contain nulls)
  • Method Details

    • refresh

      public void refresh(boolean expected)
      Actually implements contract of FileSystem.refresh().
      Overrides:
      refresh in class FileSystem
      Parameters:
      expected - should the file events be marked as expected change or not?
      See Also:
    • setDelegates

      protected final void setDelegates(FileSystem... fileSystems)
      Changes the filesystems that this system delegates to
      Parameters:
      fileSystems - array of filesystems
    • getDelegates

      protected final FileSystem[] getDelegates()
      All filesystem that this system delegates to.
      Returns:
      the array of delegates
    • getPropagateMasks

      public final boolean getPropagateMasks()
      Will mask files that are not used be listed as children?
      Returns:
      true if so
    • setPropagateMasks

      protected final void setPropagateMasks(boolean pm)
      Set whether unused mask files should be listed as children.
      Parameters:
      pm - true if so
    • isReadOnly

      public boolean isReadOnly()
      This filesystem is readonly if it has not writable system.
      Specified by:
      isReadOnly in class FileSystem
      Returns:
      true if the system is read-only
    • getDisplayName

      public String getDisplayName()
      The name of the filesystem.
      Specified by:
      getDisplayName in class FileSystem
      Returns:
      user presentable name of the filesystem
    • getRoot

      public FileObject getRoot()
      Root of the filesystem.
      Specified by:
      getRoot in class FileSystem
      Returns:
      root folder of whole filesystem
    • find

      @Deprecated public FileObject find(String aPackage, String name, String ext)
      Deprecated.
      Description copied from class: FileSystem
      Finds file in the filesystem by name.

      The default implementation converts dots in the package name into slashes, concatenates the strings, adds any extension prefixed by a dot and calls the findResource method.

      Note: when both of name and ext are null then name and extension should be ignored and scan should look only for a package.

      Overrides:
      find in class FileSystem
      Parameters:
      aPackage - package name where each package component is separated by a dot
      name - name of the file (without dots) or null if one wants to obtain a folder (package) and not a file in it
      ext - extension of the file (without leading dot) or null if one needs a package and not a file
      Returns:
      a file object that represents a file with the given name or null if the file does not exist
    • findResource

      public FileObject findResource(String name)
      Description copied from class: FileSystem
      Finds a file given its full resource path.
      Specified by:
      findResource in class FileSystem
      Parameters:
      name - the resource path, e.g. "dir/subdir/file.ext" or "dir/subdir" or "dir"
      Returns:
      a file object with the given path or null if no such file exists
    • findSystem

      protected final FileSystem findSystem(FileObject fo) throws IllegalArgumentException
      For given file object finds the filesystem that the object is placed on. The object must be created by this filesystem orherwise IllegalArgumentException is thrown.
      Parameters:
      fo - file object
      Returns:
      the filesystem (from the list we delegate to) the object has file on
      Throws:
      IllegalArgumentException - if the file object is not represented in this filesystem
    • hideResource

      protected final void hideResource(String res, boolean hide) throws IOException
      Marks a resource as hidden. It will not be listed in the list of files. Uses createMaskOn method to determine on which filesystem to mark the file.
      Parameters:
      res - resource name of file to hide or show
      hide - true if we should hide the file/false otherwise
      Throws:
      IOException - if it is not possible
    • hiddenFiles

      protected static Enumeration<String> hiddenFiles(FileObject folder, boolean rec)
      Finds all hidden files on given filesystem. The methods scans all files for ones with hidden extension and returns enumeration of names of files that are hidden.
      Parameters:
      folder - folder to start at
      rec - proceed recursivelly
      Returns:
      enumeration of String with names of hidden files
    • findResourceOn

      protected FileObject findResourceOn(FileSystem fs, String res)
      Finds a resource on given filesystem. The default implementation simply uses FileSystem.findResource, but subclasses may override this method to hide/show some resources.
      Parameters:
      fs - the filesystem to scan on
      res - the resource name to look for
      Returns:
      the file object or null
    • createWritableOn

      protected FileSystem createWritableOn(String name) throws IOException
      Finds the system to create writable version of the file on.
      Parameters:
      name - name of the file (full)
      Returns:
      the first one
      Throws:
      IOException - if the filesystem is readonly
    • createWritableOnForRename

      protected FileSystem createWritableOnForRename(String oldName, String newName) throws IOException
      Special case of createWritableOn (@see #createWritableOn).
      Parameters:
      oldName - original name of the file (full)
      newName - name new of the file (full)
      Returns:
      the first one
      Throws:
      IOException - if the filesystem is readonly
      Since:
      1.34
    • createLocksOn

      protected Set<? extends FileSystem> createLocksOn(String name) throws IOException
      When a file is about to be locked this method is consulted to choose which delegates should be locked. By default this method returns only one filesystem; the same returned by createWritableOn.

      If an delegate resides on a filesystem returned in the resulting set, it will be locked. All others will remain unlocked.

      Parameters:
      name - the resource name to lock
      Returns:
      set of filesystems
      Throws:
      IOException - if the resource cannot be locked
    • notifyMigration

      protected void notifyMigration(FileObject fo)
      Notification that a file has migrated from one filesystem to another. Usually when somebody writes to file on readonly file system and the file has to be copied to write one.

      This method allows subclasses to fire for example FileSystem.PROP_STATUS change to notify that annotation of this file should change.

      Parameters:
      fo - file object that change its actual filesystem
    • markUnimportant

      protected void markUnimportant(FileObject fo)
      Notification that a file has been marked unimportant.
      Parameters:
      fo - file object that change its actual filesystem
    • addNotify

      public void addNotify()
      Notifies all encapsulated filesystems in advance to superclass behaviour.
      Overrides:
      addNotify in class FileSystem
    • removeNotify

      public void removeNotify()
      Notifies all encapsulated filesystems in advance to superclass behaviour.
      Overrides:
      removeNotify in class FileSystem