All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.filesystems.AbstractFileSystem Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.openide.filesystems;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.StringTokenizer;
import org.openide.util.NbCollections;

/**
 * This convenience implementation does much of the hard work of
 * FileSystem and is generally more pleasant to create
 * subclasses of.
 *
 * 

It caches information about the filesystem in memory and periodically refreshes its content. Many other * operations are performed in a safer manner so as to reuse experience of NetBeans developers; should be * substantially simpler to subclass than {@link FileSystem} itself. * *

Besides what is mentioned here, the AbstractFileSystem subclass be configurable as a JavaBean * (e.g. server address, prefix, whatever it needs). * *

First of all, you need not separately implement a {@link FileObject} - this is taken care of for you. You * need only provide some information about how essential actions should be carried out. * *

You should implement {@link #getDisplayName()} to set the display name. * *

You may cause the filesystem to automatically refresh itself at periodic intervals, in case it is not * possible to be notified of changes directly. To do so, call {@link #setRefreshTime(int)}, e.g. from the * constructor. * *

{@link #refreshRoot()} must be called if the filesystem supports changing of its root directory (as the * local filesystem does, for example). * *

Most of the meat of the implementation is provided by setting appropriate values for four protected * variables, which should be initialized in the constructor; each represents an implementation of a separate * interface handling one aspect of the filesystem. * *

List member

* * The variable {@link #list} should contain an implementation of {@link AbstractFileSystem.List}. This only * specifies a way of accessing the list of children in a folder. * *

Info member

* * {@link #info} contains an {@link AbstractFileSystem.Info} which provides basic file statistics. * *

It also specifies the raw implementation of two basic types of actions: getting input and output streams * for the file; and locking and unlocking it physically (optional and not to be confused with locking within * NetBeans). * *

Change member

* * {@link #change} is an {@link AbstractFileSystem.Change} which provides the interface to create new folders * and files; delete them; and rename them (within the same directory). * *

Attribute member

* * {@link #attr} is an {@link AbstractFileSystem.Attr} allowing NetBeans to read and write serializable * attributes (meta-information) to be associated with the file. Such attributes are not much used any more, but * they are occasionally. * *

There is a default implementation in {@link DefaultAttributes} which stores attributes in a file called * .nbattrs in each folder for which a file has some attributes, using XML augmented by Java * serialization, though the regular filesystems in NetBeans now instead store all attributes globally in * $userdir/var/attributes.xml. If you do use DefaultAttributes, use it not only for * {@link #attr} but also for {@link #list} (passing in a separate private {@link AbstractFileSystem.List} * implementation to its constructor) in order to filter the .nbattrs files from view. */ public abstract class AbstractFileSystem extends FileSystem { /** generated Serialized Version UID */ private static final long serialVersionUID = -3345098214331282438L; /** cached last value of Enumeration which holds resource name (enumeration like StringTokenizer)*/ static transient private PathElements lastEnum; /** root object for the filesystem */ private transient AbstractFileObject root; /** refresher */ private transient RefreshRequest refresher; /** Provider of hierarchy of files. */ protected List list; /** Methods for modification of files. */ protected Change change; /** Methods for moving of files. This field can be left null if the filesystem * does not require special handling handling of FileObject.move and is satified * with the default implementation. */ protected Transfer transfer; /** Methods for obtaining information about files. */ protected Info info; /** Handling of attributes for files. */ protected Attr attr; /** * Actually implements contract of FileSystem.refresh(). */ public void refresh(boolean expected) { for (FileObject fo : NbCollections.iterable(getAbstractRoot().existingSubFiles(true))) { fo.refresh(expected); } } /* Provides a name for the system that can be presented to the user. * @return user presentable name of the filesystem */ public abstract String getDisplayName(); /* Getter for root folder in the filesystem. * * @return root folder of whole filesystem */ public FileObject getRoot() { return getAbstractRoot(); } /* Finds file when its resource name is given. * The name has the usual format for the {@link ClassLoader#getResource(String)} * method. So it may consist of "package1/package2/filename.ext". * If there is no package, it may consist only of "filename.ext". * * @param name resource name * * @return FileObject that represents file with given name or * null if the file does not exist */ public FileObject findResource(String name) { if (name.length() == 0) { return getAbstractRoot(); } /**Next piece of code is preformance enhancement; lastEnum = last value cache; PathElements is StringTokenizer wrapper that caches individual elements*/ PathElements local = lastEnum; if ((local == null) || !local.getOriginalName().equals(name)) { local = new PathElements(name); lastEnum = local; } return getAbstractRoot().find(local.getEnumeration()); } /** Creates Reference. In FileSystem, which subclasses AbstractFileSystem, you can overload method * createReference(FileObject fo) to achieve another type of Reference (weak, strong etc.) * @param fo is FileObject. It`s reference yourequire to get. * @return Reference to FileObject */ protected Reference createReference(T fo) { return (new WeakReference(fo)); } /** This method allows to find Reference to resourceName * @param resourceName is name of resource * @return Reference to resourceName */ protected final Reference findReference(String resourceName) { if (resourceName.length() == 0) { return null; } else { Enumeration tok = NbCollections.checkedEnumerationByFilter(new StringTokenizer(resourceName, "/"), String.class, true); // NOI18N return getAbstractRoot().findRefIfExists(tok); } } /* * @return true if RefreshAction should be enabled */ boolean isEnabledRefreshFolder() { return (refresher != null); } /** Set the number of milliseconds between automatic * refreshes of the directory structure. * * @param ms number of milliseconds between two refreshes; if <= 0 then refreshing is disabled */ protected synchronized final void setRefreshTime(int ms) { if (refresher != null) { refresher.stop(); } if ((ms <= 0) || (System.getProperty("netbeans.debug.heap") != null)) { refresher = null; } else { refresher = new RefreshRequest(this, ms); } } /** Get the number of milliseconds between automatic * refreshes of the directory structure. * By default, automatic refreshing is disabled. * @return the number of milliseconds, or 0 if refreshing is disabled */ protected final int getRefreshTime() { RefreshRequest r = refresher; return (r == null) ? 0 : r.getRefreshTime(); } /** Instruct the filesystem * that the root should change. * A fresh root is created. Subclasses that support root changes should use this. * * @return the new root */ final synchronized AbstractFileObject refreshRootImpl() { if (root != null) { root.validFlag = false; } root = createFileObject(null, ""); // NOI18N return root; } /** Instruct the filesystem * that the root should change. * A fresh root is created. Subclasses that support root changes should use this. * * @return the new root */ protected final FileObject refreshRoot() { return refreshRootImpl(); } /** Allows subclasses to fire that a change occured in a * file or folder. The change can be "expected" when it is * a result of an user action and the user knows that such * change should occur. * * @param name resource name of the file where the change occured * @param expected true if the user initiated change and expects it */ protected final void refreshResource(String name, boolean expected) { AbstractFileObject fo = (AbstractFileObject) findResourceIfExists(name); if (fo != null) { // refresh and behave like the changes is expected fo.refresh(null, null, true, expected); } } /** * For the FileObject specified as parameter, returns the recursive enumeration * of existing children fileobjects (both folders and data). It doesn't create * any new FileObject instances. Direct children are at the begining of the enumeration. * @param fo the starting point for the recursive fileobject search * @return enumeration of currently existing fileobjects. */ protected final Enumeration existingFileObjects(FileObject fo) { return existingFileObjects((AbstractFolder) fo); } final Enumeration existingFileObjects(AbstractFolder fo) { class OnlyValidAndDeep implements org.openide.util.Enumerations.Processor,FileObject> { public FileObject process(Reference obj, Collection> toAdd) { AbstractFolder file = obj.get(); if (file != null) { AbstractFolder[] arr = file.subfiles(); // make the array weak for (int i = 0; i < arr.length; i++) { toAdd.add(new WeakReference(arr[i])); } return file.isValid() ? file : null; } return null; } } Reference ref = new WeakReference(fo); Enumeration> singleEn = org.openide.util.Enumerations.>singleton(ref); return org.openide.util.Enumerations.removeNulls( org.openide.util.Enumerations.queue(singleEn, new OnlyValidAndDeep()) ); } /** * @return if value of lastModified should be cached */ boolean isLastModifiedCacheEnabled() { return true; } /* Finds file when its resource name is given. * The name has the usual format for the {@link ClassLoader#getResource(String)} * method. So it may consist of "package1/package2/filename.ext". * If there is no package, it may consist only of "filename.ext". * * @param name resource name * * @return FileObject that represents file with given name or * null if the file does not exist */ private FileObject findResourceIfExists(String name) { if (name.length() == 0) { return getAbstractRoot(); } else { Enumeration tok = NbCollections.checkedEnumerationByFilter(new StringTokenizer(name, "/"), String.class, true); // NOI18N return getAbstractRoot().findIfExists(tok); } } /** Hooking method to allow MultiFileSystem to be informed when a new * file object is created. This is the only method that creates AbstractFileObjects. * * @param parent parent object * @param name of the object */ AbstractFileObject createFileObject(AbstractFileObject parent, String name) { return new AbstractFileObject(this, parent, name); } /** * Creates root object for the fs. */ final AbstractFileObject getAbstractRoot() { synchronized (this) { if (root == null) { return refreshRootImpl(); } } return root; } /** Writes the common fields and the state of refresher. */ private void writeObject(ObjectOutputStream oos) throws IOException { ObjectOutputStream.PutField fields = oos.putFields(); fields.put("change", change); // NOI18N fields.put("info", info); // NOI18N fields.put("attr", attr); // NOI18N fields.put("list", list); // NOI18N fields.put("transfer", transfer); // NOI18N oos.writeFields(); oos.writeInt(getRefreshTime()); } /** Reads common fields and state of refresher. */ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = ois.readFields(); Object o1 = readImpl("change", fields); // change // NOI18N Object o2 = readImpl("info", fields); // info // NOI18N Object o3 = readImpl("attr", fields); // attr // NOI18N Object o4 = readImpl("list", fields); // list // NOI18N Object o5 = readImpl("transfer", fields); // transfer // NOI18N change = (Change) o1; info = (Info) o2; attr = (Attr) o3; list = (List) o4; transfer = (Transfer) o5; setRefreshTime(ois.readInt()); } // // Backward compatibility methods // /** Reads object from input stream, if it is * LocalFileSystem or JarFileSystem then replaces the object * by its Impl. */ static Object readImpl(String name, ObjectInputStream.GetField fields) throws ClassNotFoundException, IOException { Object o = fields.get(name, null); if (o instanceof LocalFileSystem) { return new LocalFileSystem.Impl((LocalFileSystem) o); } else if (o instanceof JarFileSystem) { return new JarFileSystem.Impl((JarFileSystem) o); } return o; } /** * This method is called from AbstractFileObject.isVirtual. Tests if file * really exists or is missing. Some operation on it may be restricted if returns true. * @param name of the file * @return true indicates that the file is missing. * @since 1.9 */ protected boolean checkVirtual(String name) { return false; } /** Tests if this file can be written to. * @param name resource name * @return true if this file can be written, false if not. * @since 3.31 */ protected boolean canWrite(String name) { AbstractFileObject afo = (AbstractFileObject) this.findResource(name); return (afo != null) ? afo.superCanWrite() : false; } /** Tests if this file can be read. * @param name resource name * @return true if this file can be read, false if not. * @since 3.31 */ protected boolean canRead(String name) { AbstractFileObject afo = (AbstractFileObject) this.findResource(name); return (afo != null) ? afo.superCanRead() : false; } /** Mark the file as being important or unimportant. * @param name the file to mark * @param important true indicates that file is important, false conversely * file is unimportant. * @since 1.9 */ protected void markImportant(String name, boolean important) { if (!important && (info != null)) { info.markUnimportant(name); } } /** Provides access to the hierarchy of resources. */ public interface List extends Serializable { /** @deprecated Only public by accident. */ @Deprecated /* public static final */ long serialVersionUID = -6242105832891012528L; /** Get a list of children files for a given folder. * * @param f the folder, by name; e.g. top/next/afterthat * @return a list of children of the folder, as file.ext (no path) * the array can contain null values that will be ignored */ public String[] children(String f); } /** Controls modification of files. */ public interface Change extends Serializable { /** @deprecated Only public by accident. */ @Deprecated /* public static final */ long serialVersionUID = -5841597109944924596L; /** Create new folder. * @param name full name of new folder, e.g. topfolder/newfolder * @throws IOException if the operation fails */ public void createFolder(String name) throws IOException; /** Create new data file. * * @param name full name of the file, e.g. path/from/root/filename.ext * * @exception IOException if the file cannot be created (e.g. already exists) */ public void createData(String name) throws IOException; /** Rename a file. * * @param oldName old name of the file; fully qualified * @param newName new name of the file; fully qualified * @throws IOException if it could not be renamed */ public void rename(String oldName, String newName) throws IOException; /** Delete a file. * * @param name name of file; fully qualified * @exception IOException if the file could not be deleted */ public void delete(String name) throws IOException; } /** Controls on moving of files. This is additional interface to * allow filesystem that require special handling of move to implement * it in different way then is the default one. */ public interface Transfer extends Serializable { /** @deprecated Only public by accident. */ @Deprecated /* public static final */ long serialVersionUID = -8945397853892302838L; /** Move a file. * * @param name of the file on current filesystem * @param target move implementation * @param targetName of target file * @exception IOException if the move fails * @return false if the method is not able to handle the request and * default implementation should be used instead */ public boolean move(String name, Transfer target, String targetName) throws IOException; /** Copy a file. * * @param name of the file on current filesystem * @param target target transfer implementation * @param targetName name of target file * @exception IOException if the copy fails * @return false if the method is not able to handle the request and * default implementation should be used instead */ public boolean copy(String name, Transfer target, String targetName) throws IOException; } /** Information about files. */ public interface Info extends Serializable { /** @deprecated Only public by accident. */ @Deprecated /* public static final */ long serialVersionUID = -2438286177948307985L; /** * Get last modification time. * @param name the file to test * @return the date of last modification */ public Date lastModified(String name); /** Test if the file is a folder or contains data. * @param name name of the file * @return true if the file is folder, false if it is data */ public boolean folder(String name); /** Test whether this file can be written to or not. * @param name the file to test * @return true if the file is read-only */ public boolean readOnly(String name); /** Get the MIME type of the file. If filesystem has no special support * for MIME types then can simply return null. FileSystem can register * MIME types for a well-known extensions: FileUtil.setMIMEType(String ext, String mimeType) * or together with filesystem supply some resolvers subclassed from MIMEResolver. * * @param name the file to test * @return the MIME type textual representation (e.g. "text/plain") * or null if no special support for recognizing MIME is implemented. */ public String mimeType(String name); /** Get the size of the file. * * @param name the file to test * @return the size of the file in bytes, or zero if the file does not contain data (does not * exist or is a folder). */ public long size(String name); /** Get input stream. * * @param name the file to test * @return an input stream to read the contents of this file * @exception FileNotFoundException if the file does not exist or is invalid */ public InputStream inputStream(String name) throws FileNotFoundException; /** Get output stream. * * @param name the file to test * @return output stream to overwrite the contents of this file * @exception IOException if an error occurs (the file is invalid, etc.) */ public OutputStream outputStream(String name) throws IOException; /** Lock the file. * May do nothing if the underlying storage does not support locking. * This does not affect locking using {@link FileLock} within NetBeans, however. * @param name name of the file * @throws IOException (e.g. {@link FileAlreadyLockedException}) if the file is already locked or otherwise cannot be locked */ public void lock(String name) throws IOException; /** Unlock the file. * @param name name of the file */ public void unlock(String name); /** Mark the file as being unimportant. * If not called, the file is assumed to be important. * Deprecated and new implementations need not do anything. * @param name the file to mark */ public void markUnimportant(String name); } /** * Information about files related to symbolic links. * * @since openide.filesystem/9.4 */ @SuppressWarnings("PublicInnerClass") public interface SymlinkInfo extends Serializable { /** * Check whether a file represents a symbolic link. * * @param name Name of the file. * * @return True if this file represents a symbolic link, false * otherwise. * @throws java.io.IOException If some I/O problem occurs. * @since openide.filesystem/9.4 * * @see #readSymbolicLink(java.lang.String) * @see #getCanonicalName(java.lang.String) */ boolean isSymbolicLink(String name) throws IOException; /** * Read symbolic link. * * If the file represents a symbolic link, return its target path. * Otherwise the return value is undefined, or * {@link IllegalArgumentException} can be thrown. * * This method does not perform any normalization. If the target of * symbolic link is defined using relative path, this relative path will * be returned. * * @param name Name of the file. * * @throws java.io.IOException If some I/O problem occurs. * @since openide.filesystem/9.4 * * @return Target of the symbolic link (as absolute or relative path), * or some undefined value if {@code name} does not represent symbolic * link. * * @see #isSymbolicLink(java.lang.String) */ String readSymbolicLink(String name) throws IOException; /** * Get canonical file name (resolve all symbolic links). * * @param name Name of the file. * * @return Path that represents the same file as {@code name} and * where all symbolic links are resolved. * @throws java.io.IOException If some I/O problem occurs. * @since openide.filesystem/9.4 */ String getCanonicalName(String name) throws IOException; } /** Handle attributes of files. */ public interface Attr extends Serializable { /** @deprecated Only public by accident. */ @Deprecated /* public static final */ long serialVersionUID = 5978845941846736946L; /** Get the file attribute with the specified name. * @param name the file * @param attrName name of the attribute * @return appropriate (serializable) value or null if the attribute is unset (or could not be properly restored for some reason) */ public Object readAttribute(String name, String attrName); /** Set the file attribute with the specified name. * @param name the file * @param attrName name of the attribute * @param value new value or null to clear the attribute. Must be serializable, although particular filesystems may or may not use serialization to store attribute values. * @exception IOException if the attribute cannot be set. If serialization is used to store it, this may in fact be a subclass such as {@link java.io.NotSerializableException}. */ public void writeAttribute(String name, String attrName, Object value) throws IOException; /** Get all file attribute names for the file. * @param name the file * @return enumeration of keys (as strings) */ public Enumeration attributes(String name); /** Called when a file is renamed, to appropriately update its attributes. * @param oldName old name of the file * @param newName new name of the file */ public void renameAttributes(String oldName, String newName); /** Called when a file is deleted, to also delete its attributes. * * @param name name of the file */ public void deleteAttributes(String name); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy