You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2013/11/12 15:31:46 UTC
svn commit: r1541083 [2/2] -
/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
Modified: commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java?rev=1541083&r1=1541082&r2=1541083&view=diff
==============================================================================
--- commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java (original)
+++ commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java Tue Nov 12 14:31:45 2013
@@ -69,20 +69,67 @@ public abstract class AbstractFileObject
private static final int INITIAL_LIST_SIZE = 5;
+ /**
+ * Traverses a file.
+ */
+ private static void traverse(final DefaultFileSelectorInfo fileInfo,
+ final FileSelector selector,
+ final boolean depthwise,
+ final List<FileObject> selected)
+ throws Exception
+ {
+ // Check the file itself
+ final FileObject file = fileInfo.getFile();
+ final int index = selected.size();
+
+ // If the file is a folder, traverse it
+ if (file.getType().hasChildren() && selector.traverseDescendents(fileInfo))
+ {
+ final int curDepth = fileInfo.getDepth();
+ fileInfo.setDepth(curDepth + 1);
+
+ // Traverse the children
+ final FileObject[] children = file.getChildren();
+ for (final FileObject child : children)
+ {
+ fileInfo.setFile(child);
+ traverse(fileInfo, selector, depthwise, selected);
+ }
+
+ fileInfo.setFile(file);
+ fileInfo.setDepth(curDepth);
+ }
+
+ // Add the file if doing depthwise traversal
+ if (selector.includeFile(fileInfo))
+ {
+ if (depthwise)
+ {
+ // Add this file after its descendants
+ selected.add(file);
+ }
+ else
+ {
+ // Add this file before its descendants
+ selected.add(index, file);
+ }
+ }
+ }
private final AbstractFileName fileName;
+
private final AFS fs;
private FileContent content;
-
// Cached info
private boolean attached;
private FileType type;
- private FileObject parent;
+ private FileObject parent;
// Changed to hold only the name of the children and let the object
// go into the global files cache
// private FileObject[] children;
private FileName[] children;
+
private List<Object> objects;
/**
@@ -105,1330 +152,1057 @@ public abstract class AbstractFileObject
}
/**
- * Attaches this file object to its file resource. This method is called
- * before any of the doBlah() or onBlah() methods. Sub-classes can use
- * this method to perform lazy initialisation.
- * <p/>
- * This implementation does nothing.
- * @throws Exception if an error occurs.
+ * Attaches to the file.
+ * @throws FileSystemException if an error occurs.
*/
- protected void doAttach() throws Exception
+ private void attach() throws FileSystemException
{
- }
+ synchronized (fs)
+ {
+ if (attached)
+ {
+ return;
+ }
- /**
- * Detaches this file object from its file resource.
- * <p/>
- * <p>Called when this file is closed. Note that the file object may be
- * reused later, so should be able to be reattached.
- * <p/>
- * This implementation does nothing.
- * @throws Exception if an error occurs.
- */
- protected void doDetach() throws Exception
- {
- }
+ try
+ {
+ // Attach and determine the file type
+ doAttach();
+ attached = true;
+ // now the type could already be injected by doAttach (e.g from parent to child)
- /**
- * Determines the type of this file. Must not return null. The return
- * value of this method is cached, so the implementation can be expensive.
- * @return the type of the file.
- * @throws Exception if an error occurs.
- */
- protected abstract FileType doGetType() throws Exception;
+ /* VFS-210: determine the type when really asked fore
+ if (type == null)
+ {
+ setFileType(doGetType());
+ }
+ if (type == null)
+ {
+ setFileType(FileType.IMAGINARY);
+ }
+ */
+ }
+ catch (final Exception exc)
+ {
+ throw new FileSystemException("vfs.provider/get-type.error", exc, fileName);
+ }
- /**
- * Determines if this file is executable. Is only called if {@link #doGetType}
- * does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation always returns false.
- * @return true if the file is executable, false otherwise.
- * @throws Exception if an error occurs.
- */
- protected boolean doIsExecutable() throws Exception
- {
- return false;
+ // fs.fileAttached(this);
+ }
}
/**
- * Determines if this file is hidden. Is only called if {@link #doGetType}
- * does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation always returns false.
- * @return true if the file is hidden, false otherwise.
- * @throws Exception if an error occurs.
+ * Queries the object if a simple rename to the filename of {@code newfile}
+ * is possible.
+ *
+ * @param newfile the new filename
+ * @return true if rename is possible
*/
- protected boolean doIsHidden() throws Exception
+ @Override
+ public boolean canRenameTo(final FileObject newfile)
{
- return false;
+ return fs == newfile.getFileSystem();
}
/**
- * Determines if this file can be read. Is only called if {@link #doGetType}
- * does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation always returns true.
- * @return true if the file is readable, false otherwise.
+ * Notifies the file that its children have changed.
+ * @param childName The name of the child.
+ * @param newType The type of the child.
* @throws Exception if an error occurs.
*/
- protected boolean doIsReadable() throws Exception
+ protected void childrenChanged(final FileName childName, final FileType newType) throws Exception
{
- return true;
+ // TODO - this may be called when not attached
+
+ if (children != null)
+ {
+ if (childName != null && newType != null)
+ {
+ // TODO - figure out if children[] can be replaced by list
+ final ArrayList<FileName> list = new ArrayList<FileName>(Arrays.asList(children));
+ if (newType.equals(FileType.IMAGINARY))
+ {
+ list.remove(childName);
+ }
+ else
+ {
+ list.add(childName);
+ }
+ children = new FileName[list.size()];
+ list.toArray(children);
+ }
+ }
+
+ // removeChildrenCache();
+ onChildrenChanged(childName, newType);
}
/**
- * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- *
- * @param readable
- * True to allow access, false to disallow
- * @param ownerOnly
- * If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
- * @return true if the operation succeeded
- * @see #setReadable(boolean, boolean)
- * @since 2.1
+ * Closes this file, and its content.
+ * @throws FileSystemException if an error occurs.
*/
- protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception
+ @Override
+ public void close() throws FileSystemException
{
- return false;
+ FileSystemException exc = null;
+
+ // Close the content
+ if (content != null)
+ {
+ try
+ {
+ content.close();
+ content = null;
+ }
+ catch (final FileSystemException e)
+ {
+ exc = e;
+ }
+ }
+
+ // Detach from the file
+ try
+ {
+ detach();
+ }
+ catch (final Exception e)
+ {
+ exc = new FileSystemException("vfs.provider/close.error", fileName, e);
+ }
+
+ if (exc != null)
+ {
+ throw exc;
+ }
}
/**
- * Determines if this file can be written to. Is only called if
- * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation always returns true.
- * @return true if the file is writable.
- * @throws Exception if an error occurs.
+ * Compares two FileObjects (ignores case).
+ *
+ * @param file
+ * the object to compare.
+ * @return a negative integer, zero, or a positive integer when this object is less than, equal to, or greater than
+ * the given object.
*/
- protected boolean doIsWriteable() throws Exception
+ @Override
+ public int compareTo(final FileObject file)
{
- return true;
+ if (file == null)
+ {
+ return 1;
+ }
+ return this.toString().compareToIgnoreCase(file.toString());
}
/**
- * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * Copies another file to this file.
*
- * @param writable
- * True to allow access, false to disallow
- * @param ownerOnly
- * If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
- * @return true if the operation succeeded
- * @see #setWritable(boolean, boolean)
- * @since 2.1
+ * @param file The FileObject to copy.
+ * @param selector The FileSelector.
+ * @throws FileSystemException if an error occurs.
*/
- protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception
+ @Override
+ public void copyFrom(final FileObject file, final FileSelector selector)
+ throws FileSystemException
{
- return false;
- }
-
- /**
- * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- *
- * @param writable
- * True to allow access, false to disallow
- * @param ownerOnly
- * If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
- * @return true if the operation succeeded
- * @see #setExecutable(boolean, boolean)
- * @since 2.1
- */
- protected boolean doSetExecutable(final boolean writable, final boolean ownerOnly) throws Exception
- {
- return false;
- }
+ if (!file.exists())
+ {
+ throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
+ }
+ /* we do not alway know if a file is writeable
+ if (!isWriteable())
+ {
+ throw new FileSystemException("vfs.provider/copy-read-only.error", new Object[]{file.getType(),
+ file.getName(), this}, null);
+ }
+ */
- /**
- * Lists the children of this file. Is only called if {@link #doGetType}
- * returns {@link FileType#FOLDER}. The return value of this method
- * is cached, so the implementation can be expensive.<br />
- * @return a possible empty String array if the file is a directory or null or an exception if the
- * file is not a directory or can't be read.
- * @throws Exception if an error occurs.
- */
- protected abstract String[] doListChildren() throws Exception;
+ // Locate the files to copy across
+ final ArrayList<FileObject> files = new ArrayList<FileObject>();
+ file.findFiles(selector, false, files);
- /**
- * Lists the children of this file. Is only called if {@link #doGetType}
- * returns {@link FileType#FOLDER}. The return value of this method
- * is cached, so the implementation can be expensive.<br>
- * Other than {@code doListChildren} you could return FileObject's to e.g. reinitialize the
- * type of the file.<br>
- * (Introduced for Webdav: "permission denied on resource" during getType())
- * @return The children of this FileObject.
- * @throws Exception if an error occurs.
- */
- protected FileObject[] doListChildrenResolved() throws Exception
- {
- return null;
- }
+ // Copy everything across
+ for (final FileObject srcFile : files)
+ {
+ // Determine the destination file
+ final String relPath = file.getName().getRelativeName(srcFile.getName());
+ final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);
- /**
- * Deletes the file. Is only called when:
- * <ul>
- * <li>{@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * <li>{@link #doIsWriteable} returns true.
- * <li>This file has no children, if a folder.
- * </ul>
- * <p/>
- * This implementation throws an exception.
- * @throws Exception if an error occurs.
- */
- protected void doDelete() throws Exception
- {
- throw new FileSystemException("vfs.provider/delete-not-supported.error");
- }
+ // Clean up the destination file, if necessary
+ if (destFile.exists() && destFile.getType() != srcFile.getType())
+ {
+ // The destination file exists, and is not of the same type,
+ // so delete it
+ // TODO - add a pluggable policy for deleting and overwriting existing files
+ destFile.deleteAll();
+ }
- /**
- * Renames the file. Is only called when:
- * <ul>
- * <li>{@link #doIsWriteable} returns true.
- * </ul>
- * <p/>
- * This implementation throws an exception.
- * @param newFile A FileObject with the new file name.
- * @throws Exception if an error occurs.
- */
- protected void doRename(final FileObject newFile) throws Exception
- {
- throw new FileSystemException("vfs.provider/rename-not-supported.error");
+ // Copy across
+ try
+ {
+ if (srcFile.getType().hasContent())
+ {
+ FileUtil.copyContent(srcFile, destFile);
+ }
+ else if (srcFile.getType().hasChildren())
+ {
+ destFile.createFolder();
+ }
+ }
+ catch (final IOException e)
+ {
+ throw new FileSystemException("vfs.provider/copy-file.error", e, srcFile, destFile);
+ }
+ }
}
/**
- * Creates this file as a folder. Is only called when:
- * <ul>
- * <li>{@link #doGetType} returns {@link FileType#IMAGINARY}.
- * <li>The parent folder exists and is writeable, or this file is the
- * root of the file system.
- * </ul>
- * <p/>
- * This implementation throws an exception.
- * @throws Exception if an error occurs.
+ * Creates this file, if it does not exist.
+ * @throws FileSystemException if an error occurs.
*/
- protected void doCreateFolder() throws Exception
+ @Override
+ public void createFile() throws FileSystemException
{
- throw new FileSystemException("vfs.provider/create-folder-not-supported.error");
- }
+ synchronized (fs)
+ {
+ try
+ {
+ // VFS-210: We do not want to trunc any existing file, checking for its existence is
+ // still required
+ if (exists() && !isFile())
+ {
+ throw new FileSystemException("vfs.provider/create-file.error", fileName);
+ }
- /**
- * Called when the children of this file change. Allows subclasses to
- * refresh any cached information about the children of this file.
- * <p/>
- * This implementation does nothing.
- * @param child The name of the child that changed.
- * @param newType The type of the file.
- * @throws Exception if an error occurs.
- */
- protected void onChildrenChanged(final FileName child, final FileType newType) throws Exception
- {
+ if (!exists())
+ {
+ getOutputStream().close();
+ endOutput();
+ }
+ }
+ catch (final RuntimeException re)
+ {
+ throw re;
+ }
+ catch (final Exception e)
+ {
+ throw new FileSystemException("vfs.provider/create-file.error", fileName, e);
+ }
+ }
}
/**
- * Called when the type or content of this file changes.
- * <p/>
- * This implementation does nothing.
- * @throws Exception if an error occurs.
+ * Creates this folder, if it does not exist. Also creates any ancestor
+ * files which do not exist.
+ * @throws FileSystemException if an error occurs.
*/
- protected void onChange() throws Exception
+ @Override
+ public void createFolder() throws FileSystemException
{
- }
+ synchronized (fs)
+ {
+ // VFS-210: we create a folder only if it does not already exist. So this check should be safe.
+ if (getType().hasChildren())
+ {
+ // Already exists as correct type
+ return;
+ }
+ if (getType() != FileType.IMAGINARY)
+ {
+ throw new FileSystemException("vfs.provider/create-folder-mismatched-type.error", fileName);
+ }
+ /* VFS-210: checking for writeable is not always possible as the security constraint might
+ be more complex
+ if (!isWriteable())
+ {
+ throw new FileSystemException("vfs.provider/create-folder-read-only.error", name);
+ }
+ */
- /**
- * Returns the last modified time of this file. Is only called if
- * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation throws an exception.
- * @return The last modification time.
- * @throws Exception if an error occurs.
- */
- protected long doGetLastModifiedTime() throws Exception
- {
- throw new FileSystemException("vfs.provider/get-last-modified-not-supported.error");
- }
+ // Traverse up the heirarchy and make sure everything is a folder
+ final FileObject parent = getParent();
+ if (parent != null)
+ {
+ parent.createFolder();
+ }
- /**
- * Sets the last modified time of this file. Is only called if
- * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation throws an exception.
- * @param modtime The last modification time.
- * @return true if the time was set.
- * @throws Exception if an error occurs.
- */
- protected boolean doSetLastModifiedTime(final long modtime) throws Exception
- {
- throw new FileSystemException("vfs.provider/set-last-modified-not-supported.error");
- }
+ try
+ {
+ // Create the folder
+ doCreateFolder();
- /**
- * Returns the attributes of this file. Is only called if {@link #doGetType}
- * does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation always returns an empty map.
- * @return The attributes of the file.
- * @throws Exception if an error occurs.
- */
- protected Map<String, Object> doGetAttributes() throws Exception
- {
- return Collections.emptyMap();
+ // Update cached info
+ handleCreate(FileType.FOLDER);
+ }
+ catch (final RuntimeException re)
+ {
+ throw re;
+ }
+ catch (final Exception exc)
+ {
+ throw new FileSystemException("vfs.provider/create-folder.error", fileName, exc);
+ }
+ }
}
/**
- * Sets an attribute of this file. Is only called if {@link #doGetType}
- * does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation throws an exception.
- * @param attrName The attribute name.
- * @param value The value to be associated with the attribute name.
- * @throws Exception if an error occurs.
+ * Deletes this file.
+ *
+ * @return true if this object has been deleted
+ * @todo This will not fail if this is a non-empty folder.
+ * @throws FileSystemException if an error occurs.
*/
- protected void doSetAttribute(final String attrName, final Object value) throws Exception
+ @Override
+ public boolean delete() throws FileSystemException
{
- throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
+ return delete(Selectors.SELECT_SELF) > 0;
}
/**
- * Removes an attribute of this file. Is only called if {@link #doGetType}
- * does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation throws an exception.
- * @param attrName The name of the attribute to remove.
- * @throws Exception if an error occurs.
- * @since 2.0
+ * Deletes this file, and all children matching the {@code selector}.
+ *
+ * @param selector The FileSelector.
+ * @return the number of deleted files.
+ * @throws FileSystemException if an error occurs.
*/
- protected void doRemoveAttribute(final String attrName) throws Exception
+ @Override
+ public int delete(final FileSelector selector) throws FileSystemException
{
- throw new FileSystemException("vfs.provider/remove-attribute-not-supported.error");
- }
+ int nuofDeleted = 0;
- /**
- * Returns the certificates used to sign this file. Is only called if
- * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * <p/>
- * This implementation always returns null.
- * @return The certificates used to sign the file.
- * @throws Exception if an error occurs.
- */
- protected Certificate[] doGetCertificates() throws Exception
- {
- return null;
- }
+ /* VFS-210
+ if (getType() == FileType.IMAGINARY)
+ {
+ // File does not exist
+ return nuofDeleted;
+ }
+ */
- /**
- * Returns the size of the file content (in bytes). Is only called if
- * {@link #doGetType} returns {@link FileType#FILE}.
- * @return The size of the file in bytes.
- * @throws Exception if an error occurs.
- */
- protected abstract long doGetContentSize() throws Exception;
+ // Locate all the files to delete
+ final ArrayList<FileObject> files = new ArrayList<FileObject>();
+ findFiles(selector, true, files);
- /**
- * Creates an input stream to read the file content from. Is only called
- * if {@link #doGetType} returns {@link FileType#FILE}.
- * <p/>
- * <p>It is guaranteed that there are no open output streams for this file
- * when this method is called.
- * <p/>
- * <p>The returned stream does not have to be buffered.
- * @return An InputStream to read the file content.
- * @throws Exception if an error occurs.
- */
- protected abstract InputStream doGetInputStream() throws Exception;
+ // Delete 'em
+ final int count = files.size();
+ for (int i = 0; i < count; i++)
+ {
+ final AbstractFileObject file = FileObjectUtils.getAbstractFileObject(files.get(i));
+ // file.attach();
- /**
- * Creates access to the file for random i/o. Is only called
- * if {@link #doGetType} returns {@link FileType#FILE}.
- * <p/>
- * <p>It is guaranteed that there are no open output streams for this file
- * when this method is called.
- * <p/>
- * @param mode The mode to access the file.
- * @return The RandomAccessContext.
- * @throws Exception if an error occurs.
- */
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception
- {
- throw new FileSystemException("vfs.provider/random-access-not-supported.error");
- }
+ // VFS-210: It seems impossible to me that findFiles will return a list with hidden files/directories
+ // in it, else it would not be hidden. Checking for the file-type seems ok in this case
+ // If the file is a folder, make sure all its children have been deleted
+ if (file.getType().hasChildren() && file.getChildren().length != 0)
+ {
+ // Skip - as the selector forced us not to delete all files
+ continue;
+ }
- /**
- * Creates an output stream to write the file content to. Is only
- * called if:
- * <ul>
- * <li>{@link #doIsWriteable} returns true.
- * <li>{@link #doGetType} returns {@link FileType#FILE}, or
- * {@link #doGetType} returns {@link FileType#IMAGINARY}, and the file's
- * parent exists and is a folder.
- * </ul>
- * <p/>
- * <p>It is guaranteed that there are no open stream (input or output) for
- * this file when this method is called.
- * <p/>
- * <p>The returned stream does not have to be buffered.
- * <p/>
- * This implementation throws an exception.
- * @param bAppend true if the file should be appended to, false if it should be overwritten.
- * @return An OutputStream to write to the file.
- * @throws Exception if an error occurs.
- */
- protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception
- {
- throw new FileSystemException("vfs.provider/write-not-supported.error");
- }
+ // Delete the file
+ if (file.deleteSelf())
+ {
+ nuofDeleted++;
+ }
+ }
- /**
- * Returns the URI as a String.
- *
- * @return Returns the URI as a String.
- */
- @Override
- public String toString()
- {
- return fileName.getURI();
- }
-
- /**
- * Returns the name of the file.
- * @return The FileName.
- */
- @Override
- public FileName getName()
- {
- return fileName;
+ return nuofDeleted;
}
/**
- * Returns the file system this file belongs to.
- * @return The FileSystem this file is associated with.
+ * Deletes this file and all children. Shorthand for {@code delete(Selectors.SELECT_ALL)}
+ *
+ * @return the number of deleted files.
+ * @throws FileSystemException if an error occurs.
+ * @see #delete(FileSelector)
+ * @see Selectors#SELECT_ALL
*/
@Override
- public FileSystem getFileSystem()
- {
- return fs;
- }
-
- /**
- * Returns the file system this file belongs to.
- * @return The FileSystem this file is associated with.
- */
- protected AFS getAbstractFileSystem()
+ public int deleteAll() throws FileSystemException
{
- return fs;
+ return this.delete(Selectors.SELECT_ALL);
}
/**
- * Returns a URL representation of the file.
- * @return The URL representation of the file.
+ * Deletes this file, once all its children have been deleted
+ *
+ * @return true if this file has been deleted
* @throws FileSystemException if an error occurs.
*/
- @Override
- public URL getURL() throws FileSystemException
+ private boolean deleteSelf() throws FileSystemException
{
- try
+ synchronized (fs)
{
- return AccessController.doPrivileged(new PrivilegedExceptionAction<URL>()
+ /* Its possible to delete a read-only file if you have write-execute access to the directory
+ if (!isWriteable())
{
- @Override
- public URL run() throws MalformedURLException
- {
- final StringBuilder buf = new StringBuilder();
- final String scheme = UriParser.extractScheme(fileName.getURI(), buf);
- return new URL(scheme, "", -1,
- buf.toString(), new DefaultURLStreamHandler(fs.getContext(), fs.getFileSystemOptions()));
- }
- });
- }
- catch (final PrivilegedActionException e)
- {
- throw new FileSystemException("vfs.provider/get-url.error", fileName, e.getException());
- }
- }
+ throw new FileSystemException("vfs.provider/delete-read-only.error", name);
+ }
+ */
- /**
- * Determines if the file exists.
- * @return true if the file exists, false otherwise,
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public boolean exists() throws FileSystemException
- {
- return getType() != FileType.IMAGINARY;
+ /* VFS-210
+ if (getType() == FileType.IMAGINARY)
+ {
+ // File does not exist
+ return false;
+ }
+ */
+
+ try
+ {
+ // Delete the file
+ doDelete();
+
+ // Update cached info
+ handleDelete();
+ }
+ catch (final RuntimeException re)
+ {
+ throw re;
+ }
+ catch (final Exception exc)
+ {
+ throw new FileSystemException("vfs.provider/delete.error", exc, fileName);
+ }
+
+ return true;
+ }
}
/**
- * Returns the file's type.
- * @return The FileType.
- * @throws FileSystemException if an error occurs.
+ * Detaches this file, invaliating all cached info. This will force
+ * a call to {@link #doAttach} next time this file is used.
+ * @throws Exception if an error occurs.
*/
- @Override
- public FileType getType() throws FileSystemException
+ private void detach() throws Exception
{
synchronized (fs)
{
- attach();
-
- // VFS-210: get the type only if requested for
- try
+ if (attached)
{
- if (type == null)
+ try
{
- setFileType(doGetType());
+ doDetach();
}
- if (type == null)
+ finally
{
- setFileType(FileType.IMAGINARY);
+ attached = false;
+ setFileType(null);
+ parent = null;
+
+ // fs.fileDetached(this);
+
+ removeChildrenCache();
+ // children = null;
}
}
- catch (final Exception e)
- {
- throw new FileSystemException("vfs.provider/get-type.error", e, fileName);
- }
-
- return type;
}
}
/**
- * Checks if this file is a regular file by using its file type.
- *
- * @return true if this file is a regular file.
- * @throws FileSystemException if an error occurs.
- * @see #getType()
- * @see FileType#FILE
+ * Attaches this file object to its file resource. This method is called
+ * before any of the doBlah() or onBlah() methods. Sub-classes can use
+ * this method to perform lazy initialisation.
+ * <p/>
+ * This implementation does nothing.
+ * @throws Exception if an error occurs.
*/
- @Override
- public boolean isFile() throws FileSystemException
+ protected void doAttach() throws Exception
{
- // Use equals instead of == to avoid any class loader worries.
- return FileType.FILE.equals(this.getType());
}
/**
- * Checks if this file is a folder by using its file type.
- *
- * @return true if this file is a regular file.
+ * Create a FileContent implementation.
+ * @return The FileContent.
* @throws FileSystemException if an error occurs.
- * @see #getType()
- * @see FileType#FOLDER
+ * @since 2.0
*/
- @Override
- public boolean isFolder() throws FileSystemException
+ protected FileContent doCreateFileContent() throws FileSystemException
{
- // Use equals instead of == to avoid any class loader worries.
- return FileType.FOLDER.equals(this.getType());
+ return new DefaultFileContent(this, getFileContentInfoFactory());
}
/**
- * Determines if this file is executable.
- *
- * @return {@code true} if this file is executable, {@code false} if not.
- * @throws FileSystemException On error determining if this file exists.
+ * Creates this file as a folder. Is only called when:
+ * <ul>
+ * <li>{@link #doGetType} returns {@link FileType#IMAGINARY}.
+ * <li>The parent folder exists and is writeable, or this file is the
+ * root of the file system.
+ * </ul>
+ * <p/>
+ * This implementation throws an exception.
+ * @throws Exception if an error occurs.
*/
- @Override
- public boolean isExecutable() throws FileSystemException
+ protected void doCreateFolder() throws Exception
{
- try
- {
- return exists() ? doIsExecutable() : false;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/check-is-executable.error", fileName, exc);
- }
+ throw new FileSystemException("vfs.provider/create-folder-not-supported.error");
}
/**
- * Determines if this file can be read.
- * @return true if the file is a hidden file, false otherwise.
- * @throws FileSystemException if an error occurs.
+ * Deletes the file. Is only called when:
+ * <ul>
+ * <li>{@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * <li>{@link #doIsWriteable} returns true.
+ * <li>This file has no children, if a folder.
+ * </ul>
+ * <p/>
+ * This implementation throws an exception.
+ * @throws Exception if an error occurs.
*/
- @Override
- public boolean isHidden() throws FileSystemException
+ protected void doDelete() throws Exception
{
- try
- {
- return exists() ? doIsHidden() : false;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/check-is-hidden.error", fileName, exc);
- }
+ throw new FileSystemException("vfs.provider/delete-not-supported.error");
}
/**
- * Determines if this file can be read.
- * @return true if the file can be read, false otherwise.
- * @throws FileSystemException if an error occurs.
+ * Detaches this file object from its file resource.
+ * <p/>
+ * <p>Called when this file is closed. Note that the file object may be
+ * reused later, so should be able to be reattached.
+ * <p/>
+ * This implementation does nothing.
+ * @throws Exception if an error occurs.
*/
- @Override
- public boolean isReadable() throws FileSystemException
+ protected void doDetach() throws Exception
{
- try
- {
- return exists() ? doIsReadable() : false;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/check-is-readable.error", fileName, exc);
- }
}
- @Override
- public boolean setReadable(final boolean readable, final boolean ownerOnly) throws FileSystemException
+ /**
+ * Returns the attributes of this file. Is only called if {@link #doGetType}
+ * does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation always returns an empty map.
+ * @return The attributes of the file.
+ * @throws Exception if an error occurs.
+ */
+ protected Map<String, Object> doGetAttributes() throws Exception
{
- try
- {
- return exists() ? doSetReadable(readable, ownerOnly) : false;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/set-readable.error", fileName, exc);
- }
+ return Collections.emptyMap();
}
/**
- * Determines if this file can be written to.
- * @return true if the file can be written to, false otherwise.
- * @throws FileSystemException if an error occurs.
+ * Returns the certificates used to sign this file. Is only called if
+ * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation always returns null.
+ * @return The certificates used to sign the file.
+ * @throws Exception if an error occurs.
*/
- @Override
- public boolean isWriteable() throws FileSystemException
+ protected Certificate[] doGetCertificates() throws Exception
{
- try
- {
- if (exists())
- {
- return doIsWriteable();
- }
- else
- {
- final FileObject parent = getParent();
- if (parent != null)
- {
- return parent.isWriteable();
- }
- return true;
- }
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/check-is-writeable.error", fileName, exc);
- }
+ return null;
}
- @Override
- public boolean setWritable(final boolean readable, final boolean ownerOnly) throws FileSystemException
+ /**
+ * Returns the size of the file content (in bytes). Is only called if
+ * {@link #doGetType} returns {@link FileType#FILE}.
+ * @return The size of the file in bytes.
+ * @throws Exception if an error occurs.
+ */
+ protected abstract long doGetContentSize() throws Exception;
+
+ /**
+ * Creates an input stream to read the file content from. Is only called
+ * if {@link #doGetType} returns {@link FileType#FILE}.
+ * <p/>
+ * <p>It is guaranteed that there are no open output streams for this file
+ * when this method is called.
+ * <p/>
+ * <p>The returned stream does not have to be buffered.
+ * @return An InputStream to read the file content.
+ * @throws Exception if an error occurs.
+ */
+ protected abstract InputStream doGetInputStream() throws Exception;
+
+ /**
+ * Returns the last modified time of this file. Is only called if
+ * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation throws an exception.
+ * @return The last modification time.
+ * @throws Exception if an error occurs.
+ */
+ protected long doGetLastModifiedTime() throws Exception
{
- try
- {
- return exists() ? doSetWritable(readable, ownerOnly) : false;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/set-writeable.error", fileName, exc);
- }
+ throw new FileSystemException("vfs.provider/get-last-modified-not-supported.error");
+ }
+
+ /**
+ * Creates an output stream to write the file content to. Is only
+ * called if:
+ * <ul>
+ * <li>{@link #doIsWriteable} returns true.
+ * <li>{@link #doGetType} returns {@link FileType#FILE}, or
+ * {@link #doGetType} returns {@link FileType#IMAGINARY}, and the file's
+ * parent exists and is a folder.
+ * </ul>
+ * <p/>
+ * <p>It is guaranteed that there are no open stream (input or output) for
+ * this file when this method is called.
+ * <p/>
+ * <p>The returned stream does not have to be buffered.
+ * <p/>
+ * This implementation throws an exception.
+ * @param bAppend true if the file should be appended to, false if it should be overwritten.
+ * @return An OutputStream to write to the file.
+ * @throws Exception if an error occurs.
+ */
+ protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception
+ {
+ throw new FileSystemException("vfs.provider/write-not-supported.error");
}
- @Override
- public boolean setExecutable(final boolean readable, final boolean ownerOnly) throws FileSystemException
+ /**
+ * Creates access to the file for random i/o. Is only called
+ * if {@link #doGetType} returns {@link FileType#FILE}.
+ * <p/>
+ * <p>It is guaranteed that there are no open output streams for this file
+ * when this method is called.
+ * <p/>
+ * @param mode The mode to access the file.
+ * @return The RandomAccessContext.
+ * @throws Exception if an error occurs.
+ */
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception
{
- try
- {
- return exists() ? doSetExecutable(readable, ownerOnly) : false;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/set-executable.error", fileName, exc);
- }
+ throw new FileSystemException("vfs.provider/random-access-not-supported.error");
}
/**
- * Returns an iterator over a set of all FileObject in this file object.
- *
- * @return an Iterator.
+ * Determines the type of this file. Must not return null. The return
+ * value of this method is cached, so the implementation can be expensive.
+ * @return the type of the file.
+ * @throws Exception if an error occurs.
*/
- @Override
- public Iterator<FileObject> iterator()
+ protected abstract FileType doGetType() throws Exception;
+
+ /**
+ * Determines if this file is executable. Is only called if {@link #doGetType}
+ * does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation always returns false.
+ * @return true if the file is executable, false otherwise.
+ * @throws Exception if an error occurs.
+ */
+ protected boolean doIsExecutable() throws Exception
{
- try
- {
- return listFiles(Selectors.SELECT_ALL).iterator();
- }
- catch (final FileSystemException e)
- {
- throw new IllegalStateException(e);
- }
+ return false;
}
-
+
/**
- * Lists the set of matching descendants of this file, in depthwise
- * order.
- *
- * @param selector The FileSelector.
- * @return list of files or null if the base file (this object) do not exist or the {@code selector} is null
- * @throws FileSystemException if an error occurs.
+ * Determines if this file is hidden. Is only called if {@link #doGetType}
+ * does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation always returns false.
+ * @return true if the file is hidden, false otherwise.
+ * @throws Exception if an error occurs.
*/
- public List<FileObject> listFiles(final FileSelector selector) throws FileSystemException
+ protected boolean doIsHidden() throws Exception
{
- if (!exists() || selector == null)
- {
- return null;
- }
-
- final ArrayList<FileObject> list = new ArrayList<FileObject>();
- this.findFiles(selector, true, list);
- return list;
+ return false;
}
-
/**
- * Returns the parent of the file.
- * @return the parent FileObject.
- * @throws FileSystemException if an error occurs.
+ * Determines if this file can be read. Is only called if {@link #doGetType}
+ * does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation always returns true.
+ * @return true if the file is readable, false otherwise.
+ * @throws Exception if an error occurs.
*/
- @Override
- public FileObject getParent() throws FileSystemException
+ protected boolean doIsReadable() throws Exception
{
- if (this == fs.getRoot())
- {
- if (fs.getParentLayer() == null)
- {
- // Root file has no parent
- return null;
- }
- else
- {
- // Return the parent of the parent layer
- return fs.getParentLayer().getParent();
- }
- }
-
- synchronized (fs)
- {
- // Locate the parent of this file
- if (parent == null)
- {
- parent = fs.resolveFile(fileName.getParent());
- }
- }
- return parent;
+ return true;
}
/**
- * Returns the children of the file.
- * @return an array of FileObjects, one per child.
+ * Checks if this fileObject is the same file as {@code destFile} just with a different
+ * name.<br />
+ * E.g. for case insensitive filesystems like windows.
+ * @param destFile The file to compare to.
+ * @return true if the FileObjects are the same.
* @throws FileSystemException if an error occurs.
*/
- @Override
- public FileObject[] getChildren() throws FileSystemException
+ protected boolean doIsSameFile(final FileObject destFile) throws FileSystemException
{
- synchronized (fs)
- {
- // VFS-210
- if (!fs.hasCapability(Capability.LIST_CHILDREN))
- {
- throw new FileNotFolderException(fileName);
- }
-
- /* VFS-210
- if (!getType().hasChildren())
- {
- throw new FileSystemException("vfs.provider/list-children-not-folder.error", name);
- }
- */
- attach();
-
- // Use cached info, if present
- if (children != null)
- {
- return resolveFiles(children);
- }
-
- // allow the filesystem to return resolved children. e.g. prefill type for webdav
- FileObject[] childrenObjects;
- try
- {
- childrenObjects = doListChildrenResolved();
- children = extractNames(childrenObjects);
- }
- catch (final FileSystemException exc)
- {
- // VFS-210
- throw exc;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
- }
-
- if (childrenObjects != null)
- {
- return childrenObjects;
- }
-
- // List the children
- final String[] files;
- try
- {
- files = doListChildren();
- }
- catch (final FileSystemException exc)
- {
- // VFS-210
- throw exc;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
- }
-
- if (files == null)
- {
- // VFS-210
- // honor the new doListChildren contract
- // return null;
- throw new FileNotFolderException(fileName);
- }
- else if (files.length == 0)
- {
- // No children
- children = EMPTY_FILE_ARRAY;
- }
- else
- {
- // Create file objects for the children
- // children = new FileObject[files.length];
- final FileName[] cache = new FileName[files.length];
- for (int i = 0; i < files.length; i++)
- {
- final String file = files[i];
- // children[i] = fs.resolveFile(name.resolveName(file, NameScope.CHILD));
- // children[i] = name.resolveName(file, NameScope.CHILD);
- cache[i] = fs.getFileSystemManager().resolveName(fileName, file, NameScope.CHILD);
- }
- // VFS-285: only assign the children filenames after all of them have been
- // resolved successfully to prevent an inconsistent internal state
- children = cache;
- }
-
- return resolveFiles(children);
- }
+ return false;
}
- private FileName[] extractNames(final FileObject[] objects)
+ /**
+ * Determines if this file can be written to. Is only called if
+ * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation always returns true.
+ * @return true if the file is writable.
+ * @throws Exception if an error occurs.
+ */
+ protected boolean doIsWriteable() throws Exception
{
- if (objects == null)
- {
- return null;
- }
+ return true;
+ }
- final FileName[] names = new FileName[objects.length];
- for (int iterObjects = 0; iterObjects < objects.length; iterObjects++)
- {
- names[iterObjects] = objects[iterObjects].getName();
- }
+ /**
+ * Lists the children of this file. Is only called if {@link #doGetType}
+ * returns {@link FileType#FOLDER}. The return value of this method
+ * is cached, so the implementation can be expensive.<br />
+ * @return a possible empty String array if the file is a directory or null or an exception if the
+ * file is not a directory or can't be read.
+ * @throws Exception if an error occurs.
+ */
+ protected abstract String[] doListChildren() throws Exception;
- return names;
+ /**
+ * Lists the children of this file. Is only called if {@link #doGetType}
+ * returns {@link FileType#FOLDER}. The return value of this method
+ * is cached, so the implementation can be expensive.<br>
+ * Other than {@code doListChildren} you could return FileObject's to e.g. reinitialize the
+ * type of the file.<br>
+ * (Introduced for Webdav: "permission denied on resource" during getType())
+ * @return The children of this FileObject.
+ * @throws Exception if an error occurs.
+ */
+ protected FileObject[] doListChildrenResolved() throws Exception
+ {
+ return null;
}
- private FileObject[] resolveFiles(final FileName[] children) throws FileSystemException
+ /**
+ * Removes an attribute of this file. Is only called if {@link #doGetType}
+ * does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation throws an exception.
+ * @param attrName The name of the attribute to remove.
+ * @throws Exception if an error occurs.
+ * @since 2.0
+ */
+ protected void doRemoveAttribute(final String attrName) throws Exception
{
- if (children == null)
- {
- return null;
- }
-
- final FileObject[] objects = new FileObject[children.length];
- for (int iterChildren = 0; iterChildren < children.length; iterChildren++)
- {
- objects[iterChildren] = resolveFile(children[iterChildren]);
- }
+ throw new FileSystemException("vfs.provider/remove-attribute-not-supported.error");
+ }
- return objects;
+ /**
+ * Renames the file. Is only called when:
+ * <ul>
+ * <li>{@link #doIsWriteable} returns true.
+ * </ul>
+ * <p/>
+ * This implementation throws an exception.
+ * @param newFile A FileObject with the new file name.
+ * @throws Exception if an error occurs.
+ */
+ protected void doRename(final FileObject newFile) throws Exception
+ {
+ throw new FileSystemException("vfs.provider/rename-not-supported.error");
}
- private FileObject resolveFile(final FileName child) throws FileSystemException
+ /**
+ * Sets an attribute of this file. Is only called if {@link #doGetType}
+ * does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation throws an exception.
+ * @param attrName The attribute name.
+ * @param value The value to be associated with the attribute name.
+ * @throws Exception if an error occurs.
+ */
+ protected void doSetAttribute(final String attrName, final Object value) throws Exception
{
- return fs.resolveFile(child);
+ throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
}
/**
- * Returns a child of this file.
- * @param name The name of the child to locate.
- * @return The FileObject for the file or null if the child does not exist.
- * @throws FileSystemException if an error occurs.
+ * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ *
+ * @param writable
+ * True to allow access, false to disallow
+ * @param ownerOnly
+ * If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
+ * @return true if the operation succeeded
+ * @see #setExecutable(boolean, boolean)
+ * @since 2.1
*/
- @Override
- public FileObject getChild(final String name) throws FileSystemException
+ protected boolean doSetExecutable(final boolean writable, final boolean ownerOnly) throws Exception
{
- // TODO - use a hashtable when there are a large number of children
- final FileObject[] children = getChildren();
- for (final FileObject element : children)
- {
- // final FileObject child = children[i];
- final FileName child = element.getName();
- // TODO - use a comparator to compare names
- // if (child.getName().getBaseName().equals(name))
- if (child.getBaseName().equals(name))
- {
- return resolveFile(child);
- }
- }
- return null;
+ return false;
}
/**
- * Returns a child by name.
- * @param name The name of the child to locate.
- * @param scope the NameScope.
- * @return The FileObject for the file or null if the child does not exist.
- * @throws FileSystemException if an error occurs.
+ * Sets the last modified time of this file. Is only called if
+ * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * <p/>
+ * This implementation throws an exception.
+ * @param modtime The last modification time.
+ * @return true if the time was set.
+ * @throws Exception if an error occurs.
*/
- @Override
- public FileObject resolveFile(final String name, final NameScope scope)
- throws FileSystemException
+ protected boolean doSetLastModifiedTime(final long modtime) throws Exception
{
- // return fs.resolveFile(this.name.resolveName(name, scope));
- return fs.resolveFile(fs.getFileSystemManager().resolveName(this.fileName, name, scope));
+ throw new FileSystemException("vfs.provider/set-last-modified-not-supported.error");
}
/**
- * Finds a file, relative to this file.
+ * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
*
- * @param path The path of the file to locate. Can either be a relative
- * path, which is resolved relative to this file, or an
- * absolute path, which is resolved relative to the file system
- * that contains this file.
- * @return The FileObject.
- * @throws FileSystemException if an error occurs.
+ * @param readable
+ * True to allow access, false to disallow
+ * @param ownerOnly
+ * If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
+ * @return true if the operation succeeded
+ * @see #setReadable(boolean, boolean)
+ * @since 2.1
*/
- @Override
- public FileObject resolveFile(final String path) throws FileSystemException
+ protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception
{
- final FileName otherName = fs.getFileSystemManager().resolveName(fileName, path);
- return fs.resolveFile(otherName);
+ return false;
}
/**
- * Deletes this file, once all its children have been deleted
+ * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
*
- * @return true if this file has been deleted
- * @throws FileSystemException if an error occurs.
+ * @param writable
+ * True to allow access, false to disallow
+ * @param ownerOnly
+ * If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
+ * @return true if the operation succeeded
+ * @see #setWritable(boolean, boolean)
+ * @since 2.1
*/
- private boolean deleteSelf() throws FileSystemException
+ protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception
{
- synchronized (fs)
- {
- /* Its possible to delete a read-only file if you have write-execute access to the directory
- if (!isWriteable())
- {
- throw new FileSystemException("vfs.provider/delete-read-only.error", name);
- }
- */
-
- /* VFS-210
- if (getType() == FileType.IMAGINARY)
- {
- // File does not exist
- return false;
- }
- */
-
- try
- {
- // Delete the file
- doDelete();
-
- // Update cached info
- handleDelete();
- }
- catch (final RuntimeException re)
- {
- throw re;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/delete.error", exc, fileName);
- }
-
- return true;
- }
+ return false;
}
/**
- * Deletes this file.
- *
- * @return true if this object has been deleted
- * @todo This will not fail if this is a non-empty folder.
- * @throws FileSystemException if an error occurs.
+ * Called when the ouput stream for this file is closed.
+ * @throws Exception if an error occurs.
*/
- @Override
- public boolean delete() throws FileSystemException
+ protected void endOutput() throws Exception
{
- return delete(Selectors.SELECT_SELF) > 0;
+ if (getType() == FileType.IMAGINARY)
+ {
+ // File was created
+ handleCreate(FileType.FILE);
+ }
+ else
+ {
+ // File has changed
+ onChange();
+ }
}
/**
- * Deletes this file, and all children matching the {@code selector}.
- *
- * @param selector The FileSelector.
- * @return the number of deleted files.
+ * Determines if the file exists.
+ * @return true if the file exists, false otherwise,
* @throws FileSystemException if an error occurs.
*/
@Override
- public int delete(final FileSelector selector) throws FileSystemException
+ public boolean exists() throws FileSystemException
{
- int nuofDeleted = 0;
+ return getType() != FileType.IMAGINARY;
+ }
- /* VFS-210
- if (getType() == FileType.IMAGINARY)
+ private FileName[] extractNames(final FileObject[] objects)
+ {
+ if (objects == null)
{
- // File does not exist
- return nuofDeleted;
+ return null;
}
- */
-
- // Locate all the files to delete
- final ArrayList<FileObject> files = new ArrayList<FileObject>();
- findFiles(selector, true, files);
- // Delete 'em
- final int count = files.size();
- for (int i = 0; i < count; i++)
+ final FileName[] names = new FileName[objects.length];
+ for (int iterObjects = 0; iterObjects < objects.length; iterObjects++)
{
- final AbstractFileObject file = FileObjectUtils.getAbstractFileObject(files.get(i));
- // file.attach();
+ names[iterObjects] = objects[iterObjects].getName();
+ }
- // VFS-210: It seems impossible to me that findFiles will return a list with hidden files/directories
- // in it, else it would not be hidden. Checking for the file-type seems ok in this case
- // If the file is a folder, make sure all its children have been deleted
- if (file.getType().hasChildren() && file.getChildren().length != 0)
- {
- // Skip - as the selector forced us not to delete all files
- continue;
- }
+ return names;
+ }
- // Delete the file
- if (file.deleteSelf())
- {
- nuofDeleted++;
- }
- }
+ @Override
+ protected void finalize() throws Throwable
+ {
+ fs.fileObjectDestroyed(this);
- return nuofDeleted;
+ super.finalize();
}
+
/**
- * Deletes this file and all children. Shorthand for {@code delete(Selectors.SELECT_ALL)}
+ * Finds the set of matching descendants of this file, in depthwise
+ * order.
*
- * @return the number of deleted files.
+ * @param selector The FileSelector.
+ * @return list of files or null if the base file (this object) do not exist
* @throws FileSystemException if an error occurs.
- * @see #delete(FileSelector)
- * @see Selectors#SELECT_ALL
*/
@Override
- public int deleteAll() throws FileSystemException
+ public FileObject[] findFiles(final FileSelector selector) throws FileSystemException
{
- return this.delete(Selectors.SELECT_ALL);
+ final List<FileObject> list = this.listFiles(selector);
+ return list == null ? null : list.toArray(new FileObject[list.size()]);
}
/**
- * Creates this file, if it does not exist.
+ * Traverses the descendants of this file, and builds a list of selected
+ * files.
+ * @param selector The FileSelector.
+ * @param depthwise if true files are added after their descendants, before otherwise.
+ * @param selected A List of the located FileObjects.
* @throws FileSystemException if an error occurs.
*/
@Override
- public void createFile() throws FileSystemException
+ public void findFiles(final FileSelector selector,
+ final boolean depthwise,
+ final List<FileObject> selected) throws FileSystemException
{
- synchronized (fs)
+ try
{
- try
- {
- // VFS-210: We do not want to trunc any existing file, checking for its existence is
- // still required
- if (exists() && !isFile())
- {
- throw new FileSystemException("vfs.provider/create-file.error", fileName);
- }
-
- if (!exists())
- {
- getOutputStream().close();
- endOutput();
- }
- }
- catch (final RuntimeException re)
- {
- throw re;
- }
- catch (final Exception e)
+ if (exists())
{
- throw new FileSystemException("vfs.provider/create-file.error", fileName, e);
+ // Traverse starting at this file
+ final DefaultFileSelectorInfo info = new DefaultFileSelectorInfo();
+ info.setBaseFolder(this);
+ info.setDepth(0);
+ info.setFile(this);
+ traverse(info, selector, depthwise, selected);
}
}
+ catch (final Exception e)
+ {
+ throw new FileSystemException("vfs.provider/find-files.error", fileName, e);
+ }
}
/**
- * Creates this folder, if it does not exist. Also creates any ancestor
- * files which do not exist.
- * @throws FileSystemException if an error occurs.
+ * Returns the file system this file belongs to.
+ * @return The FileSystem this file is associated with.
*/
- @Override
- public void createFolder() throws FileSystemException
+ protected AFS getAbstractFileSystem()
{
- synchronized (fs)
- {
- // VFS-210: we create a folder only if it does not already exist. So this check should be safe.
- if (getType().hasChildren())
- {
- // Already exists as correct type
- return;
- }
- if (getType() != FileType.IMAGINARY)
- {
- throw new FileSystemException("vfs.provider/create-folder-mismatched-type.error", fileName);
- }
- /* VFS-210: checking for writeable is not always possible as the security constraint might
- be more complex
- if (!isWriteable())
- {
- throw new FileSystemException("vfs.provider/create-folder-read-only.error", name);
- }
- */
-
- // Traverse up the heirarchy and make sure everything is a folder
- final FileObject parent = getParent();
- if (parent != null)
- {
- parent.createFolder();
- }
-
- try
- {
- // Create the folder
- doCreateFolder();
-
- // Update cached info
- handleCreate(FileType.FOLDER);
- }
- catch (final RuntimeException re)
- {
- throw re;
- }
- catch (final Exception exc)
- {
- throw new FileSystemException("vfs.provider/create-folder.error", fileName, exc);
- }
- }
+ return fs;
}
/**
- * Compares two FileObjects (ignores case).
- *
- * @param file
- * the object to compare.
- * @return a negative integer, zero, or a positive integer when this object is less than, equal to, or greater than
- * the given object.
+ * Returns a child of this file.
+ * @param name The name of the child to locate.
+ * @return The FileObject for the file or null if the child does not exist.
+ * @throws FileSystemException if an error occurs.
*/
@Override
- public int compareTo(final FileObject file)
+ public FileObject getChild(final String name) throws FileSystemException
{
- if (file == null)
+ // TODO - use a hashtable when there are a large number of children
+ final FileObject[] children = getChildren();
+ for (final FileObject element : children)
{
- return 1;
+ // final FileObject child = children[i];
+ final FileName child = element.getName();
+ // TODO - use a comparator to compare names
+ // if (child.getName().getBaseName().equals(name))
+ if (child.getBaseName().equals(name))
+ {
+ return resolveFile(child);
+ }
}
- return this.toString().compareToIgnoreCase(file.toString());
+ return null;
}
/**
- * Copies another file to this file.
- *
- * @param file The FileObject to copy.
- * @param selector The FileSelector.
+ * Returns the children of the file.
+ * @return an array of FileObjects, one per child.
* @throws FileSystemException if an error occurs.
*/
@Override
- public void copyFrom(final FileObject file, final FileSelector selector)
- throws FileSystemException
+ public FileObject[] getChildren() throws FileSystemException
{
- if (!file.exists())
- {
- throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
- }
- /* we do not alway know if a file is writeable
- if (!isWriteable())
+ synchronized (fs)
{
- throw new FileSystemException("vfs.provider/copy-read-only.error", new Object[]{file.getType(),
- file.getName(), this}, null);
- }
- */
-
- // Locate the files to copy across
- final ArrayList<FileObject> files = new ArrayList<FileObject>();
- file.findFiles(selector, false, files);
+ // VFS-210
+ if (!fs.hasCapability(Capability.LIST_CHILDREN))
+ {
+ throw new FileNotFolderException(fileName);
+ }
- // Copy everything across
- for (final FileObject srcFile : files)
- {
- // Determine the destination file
- final String relPath = file.getName().getRelativeName(srcFile.getName());
- final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);
+ /* VFS-210
+ if (!getType().hasChildren())
+ {
+ throw new FileSystemException("vfs.provider/list-children-not-folder.error", name);
+ }
+ */
+ attach();
- // Clean up the destination file, if necessary
- if (destFile.exists() && destFile.getType() != srcFile.getType())
+ // Use cached info, if present
+ if (children != null)
{
- // The destination file exists, and is not of the same type,
- // so delete it
- // TODO - add a pluggable policy for deleting and overwriting existing files
- destFile.deleteAll();
+ return resolveFiles(children);
}
- // Copy across
+ // allow the filesystem to return resolved children. e.g. prefill type for webdav
+ FileObject[] childrenObjects;
try
{
- if (srcFile.getType().hasContent())
- {
- FileUtil.copyContent(srcFile, destFile);
- }
- else if (srcFile.getType().hasChildren())
- {
- destFile.createFolder();
- }
+ childrenObjects = doListChildrenResolved();
+ children = extractNames(childrenObjects);
}
- catch (final IOException e)
+ catch (final FileSystemException exc)
{
- throw new FileSystemException("vfs.provider/copy-file.error", e, srcFile, destFile);
+ // VFS-210
+ throw exc;
}
- }
- }
-
- /**
- * Moves (rename) the file to another one.
- * @param destFile The target FileObject.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void moveTo(final FileObject destFile) throws FileSystemException
- {
- if (canRenameTo(destFile))
- {
- if (!getParent().isWriteable())
+ catch (final Exception exc)
{
- throw new FileSystemException("vfs.provider/rename-parent-read-only.error",
- getName(),
- getParent().getName());
+ throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
}
- }
- else
- {
- if (!isWriteable())
+
+ if (childrenObjects != null)
{
- throw new FileSystemException("vfs.provider/rename-read-only.error", getName());
+ return childrenObjects;
}
- }
-
- if (destFile.exists() && !isSameFile(destFile))
- {
- destFile.deleteAll();
- // throw new FileSystemException("vfs.provider/rename-dest-exists.error", destFile.getName());
- }
- if (canRenameTo(destFile))
- {
- // issue rename on same filesystem
+ // List the children
+ final String[] files;
try
{
- attach();
- doRename(destFile);
-
- FileObjectUtils.getAbstractFileObject(destFile).handleCreate(getType());
-
- destFile.close(); // now the destFile is no longer imaginary. force reattach.
-
- handleDelete(); // fire delete-events. This file-object (src) is like deleted.
+ files = doListChildren();
}
- catch (final RuntimeException re)
+ catch (final FileSystemException exc)
{
- throw re;
+ // VFS-210
+ throw exc;
}
catch (final Exception exc)
{
- throw new FileSystemException("vfs.provider/rename.error", exc,
- getName(),
- destFile.getName());
+ throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
}
- }
- else
- {
- // different fs - do the copy/delete stuff
- destFile.copyFrom(this, Selectors.SELECT_SELF);
-
- if ((destFile.getType().hasContent()
- && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FILE)
- || destFile.getType().hasChildren()
- && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FOLDER))
- && fs.hasCapability(Capability.GET_LAST_MODIFIED))
+ if (files == null)
{
- destFile.getContent().setLastModifiedTime(this.getContent().getLastModifiedTime());
+ // VFS-210
+ // honor the new doListChildren contract
+ // return null;
+ throw new FileNotFolderException(fileName);
+ }
+ else if (files.length == 0)
+ {
+ // No children
+ children = EMPTY_FILE_ARRAY;
+ }
+ else
+ {
+ // Create file objects for the children
+ // children = new FileObject[files.length];
+ final FileName[] cache = new FileName[files.length];
+ for (int i = 0; i < files.length; i++)
+ {
+ final String file = files[i];
+ // children[i] = fs.resolveFile(name.resolveName(file, NameScope.CHILD));
+ // children[i] = name.resolveName(file, NameScope.CHILD);
+ cache[i] = fs.getFileSystemManager().resolveName(fileName, file, NameScope.CHILD);
+ }
+ // VFS-285: only assign the children filenames after all of them have been
+ // resolved successfully to prevent an inconsistent internal state
+ children = cache;
}
- deleteSelf();
+ return resolveFiles(children);
}
-
- }
-
- /**
- * Checks if this fileObject is the same file as {@code destFile} just with a different
- * name.<br />
- * E.g. for case insensitive filesystems like windows.
- * @param destFile The file to compare to.
- * @return true if the FileObjects are the same.
- * @throws FileSystemException if an error occurs.
- */
- protected boolean isSameFile(final FileObject destFile) throws FileSystemException
- {
- attach();
- return doIsSameFile(destFile);
- }
-
- /**
- * Checks if this fileObject is the same file as {@code destFile} just with a different
- * name.<br />
- * E.g. for case insensitive filesystems like windows.
- * @param destFile The file to compare to.
- * @return true if the FileObjects are the same.
- * @throws FileSystemException if an error occurs.
- */
- protected boolean doIsSameFile(final FileObject destFile) throws FileSystemException
- {
- return false;
- }
-
- /**
- * Queries the object if a simple rename to the filename of {@code newfile}
- * is possible.
- *
- * @param newfile the new filename
- * @return true if rename is possible
- */
- @Override
- public boolean canRenameTo(final FileObject newfile)
- {
- return fs == newfile.getFileSystem();
- }
-
- /**
- * Finds the set of matching descendants of this file, in depthwise
- * order.
- *
- * @param selector The FileSelector.
- * @return list of files or null if the base file (this object) do not exist
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject[] findFiles(final FileSelector selector) throws FileSystemException
- {
- final List<FileObject> list = this.listFiles(selector);
- return list == null ? null : list.toArray(new FileObject[list.size()]);
}
/**
@@ -1451,71 +1225,38 @@ public abstract class AbstractFileObject
}
/**
- * Create a FileContent implementation.
- * @return The FileContent.
- * @throws FileSystemException if an error occurs.
- * @since 2.0
+ * create the filecontentinfo implementation.
+ * @return The FileContentInfoFactory.
*/
- protected FileContent doCreateFileContent() throws FileSystemException
+ protected FileContentInfoFactory getFileContentInfoFactory()
{
- return new DefaultFileContent(this, getFileContentInfoFactory());
+ return fs.getFileSystemManager().getFileContentInfoFactory();
}
/**
- * This will prepare the fileObject to get resynchronized with the underlaying filesystem if required.
+ * @return FileOperations interface that provides access to the operations
+ * API.
* @throws FileSystemException if an error occurs.
*/
@Override
- public void refresh() throws FileSystemException
+ public FileOperations getFileOperations() throws FileSystemException
{
- // Detach from the file
- try
- {
- detach();
- }
- catch (final Exception e)
+ if (operations == null)
{
- throw new FileSystemException("vfs.provider/resync.error", fileName, e);
+ operations = new DefaultFileOperations(this);
}
+
+ return operations;
}
/**
- * Closes this file, and its content.
- * @throws FileSystemException if an error occurs.
+ * Returns the file system this file belongs to.
+ * @return The FileSystem this file is associated with.
*/
@Override
- public void close() throws FileSystemException
+ public FileSystem getFileSystem()
{
- FileSystemException exc = null;
-
- // Close the content
- if (content != null)
- {
- try
- {
- content.close();
- content = null;
- }
- catch (final FileSystemException e)
- {
- exc = e;
- }
- }
-
- // Detach from the file
- try
- {
- detach();
- }
- catch (final Exception e)
- {
- exc = new FileSystemException("vfs.provider/close.error", fileName, e);
- }
-
- if (exc != null)
- {
- throw exc;
- }
+ return fs;
}
/**
@@ -1551,12 +1292,122 @@ public abstract class AbstractFileObject
}
catch (final FileSystemException exc)
{
- throw exc;
+ throw exc;
+ }
+ catch (final Exception exc)
+ {
+ throw new FileSystemException("vfs.provider/read.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Returns the name of the file.
+ * @return The FileName.
+ */
+ @Override
+ public FileName getName()
+ {
+ return fileName;
+ }
+
+ /**
+ * Prepares this file for writing. Makes sure it is either a file,
+ * or its parent folder exists. Returns an output stream to use to
+ * write the content of the file to.
+ * @return An OutputStream where the new contents of the file can be written.
+ * @throws FileSystemException if an error occurs.
+ */
+ public OutputStream getOutputStream() throws FileSystemException
+ {
+ return getOutputStream(false);
+ }
+
+ /**
+ * Prepares this file for writing. Makes sure it is either a file,
+ * or its parent folder exists. Returns an output stream to use to
+ * write the content of the file to.<br>
+ *
+ * @param bAppend true when append to the file.<br>
+ * Note: If the underlaying filesystem does not support appending,
+ * a FileSystemException is thrown.
+ * @return An OutputStream where the new contents of the file can be written.
+ * @throws FileSystemException if an error occurs; for example:<br>
+ * bAppend is true, and the unbderlying FileSystem does not support it
+ */
+ public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException
+ {
+ /* VFS-210
+ if (getType() != FileType.IMAGINARY && !getType().hasContent())
+ {
+ throw new FileSystemException("vfs.provider/write-not-file.error", name);
+ }
+ if (!isWriteable())
+ {
+ throw new FileSystemException("vfs.provider/write-read-only.error", name);
+ }
+ */
+
+ if (bAppend && !fs.hasCapability(Capability.APPEND_CONTENT))
+ {
+ throw new FileSystemException("vfs.provider/write-append-not-supported.error", fileName);
+ }
+
+ if (getType() == FileType.IMAGINARY)
+ {
+// Does not exist - make sure parent does
+ final FileObject parent = getParent();
+ if (parent != null)
+ {
+ parent.createFolder();
+ }
+ }
+
+// Get the raw output stream
+ try
+ {
+ return doGetOutputStream(bAppend);
+ }
+ catch (final RuntimeException re)
+ {
+ throw re;
+ }
+ catch (final Exception exc)
+ {
+ throw new FileSystemException("vfs.provider/write.error", exc, fileName);
+ }
+ }
+
+ /**
+ * Returns the parent of the file.
+ * @return the parent FileObject.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject getParent() throws FileSystemException
+ {
+ if (this == fs.getRoot())
+ {
+ if (fs.getParentLayer() == null)
+ {
+ // Root file has no parent
+ return null;
+ }
+ else
+ {
+ // Return the parent of the parent layer
+ return fs.getParentLayer().getParent();
+ }
}
- catch (final Exception exc)
+
+ synchronized (fs)
{
- throw new FileSystemException("vfs.provider/read.error", fileName, exc);
+ // Locate the parent of this file
+ if (parent == null)
+ {
+ parent = fs.resolveFile(fileName.getParent());
+ }
}
+ return parent;
}
/**
@@ -1611,276 +1462,432 @@ public abstract class AbstractFileObject
}
/**
- * Prepares this file for writing. Makes sure it is either a file,
- * or its parent folder exists. Returns an output stream to use to
- * write the content of the file to.
- * @return An OutputStream where the new contents of the file can be written.
+ * Returns the file's type.
+ * @return The FileType.
* @throws FileSystemException if an error occurs.
*/
- public OutputStream getOutputStream() throws FileSystemException
+ @Override
+ public FileType getType() throws FileSystemException
{
- return getOutputStream(false);
+ synchronized (fs)
+ {
+ attach();
+
+ // VFS-210: get the type only if requested for
+ try
+ {
+ if (type == null)
+ {
+ setFileType(doGetType());
+ }
+ if (type == null)
+ {
+ setFileType(FileType.IMAGINARY);
+ }
+ }
+ catch (final Exception e)
+ {
+ throw new FileSystemException("vfs.provider/get-type.error", e, fileName);
+ }
+
+ return type;
+ }
}
/**
- * Prepares this file for writing. Makes sure it is either a file,
- * or its parent folder exists. Returns an output stream to use to
- * write the content of the file to.<br>
- *
- * @param bAppend true when append to the file.<br>
- * Note: If the underlaying filesystem does not support appending,
- * a FileSystemException is thrown.
- * @return An OutputStream where the new contents of the file can be written.
- * @throws FileSystemException if an error occurs; for example:<br>
- * bAppend is true, and the unbderlying FileSystem does not support it
+ * Returns a URL representation of the file.
+ * @return The URL representation of the file.
+ * @throws FileSystemException if an error occurs.
*/
- public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException
+ @Override
+ public URL getURL() throws FileSystemException
{
- /* VFS-210
- if (getType() != FileType.IMAGINARY && !getType().hasContent())
+ try
{
- throw new FileSystemException("vfs.provider/write-not-file.error", name);
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<URL>()
+ {
+ @Override
+ public URL run() throws MalformedURLException
+ {
+ final StringBuilder buf = new StringBuilder();
+ final String scheme = UriParser.extractScheme(fileName.getURI(), buf);
+ return new URL(scheme, "", -1,
+ buf.toString(), new DefaultURLStreamHandler(fs.getContext(), fs.getFileSystemOptions()));
+ }
+ });
}
- if (!isWriteable())
+ catch (final PrivilegedActionException e)
{
- throw new FileSystemException("vfs.provider/write-read-only.error", name);
+ throw new FileSystemException("vfs.provider/get-url.error", fileName, e.getException());
}
- */
+ }
- if (bAppend && !fs.hasCapability(Capability.APPEND_CONTENT))
+ /**
+ * Called when this file is changed.<br />
+ * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}.
+ * @throws Exception if an error occurs.
+ */
+ protected void handleChanged() throws Exception
+ {
+ // Notify the file system
+ fs.fireFileChanged(this);
+ }
+
+ /**
+ * Called when this file is created. Updates cached info and notifies
+ * the parent and file system.
+ * @param newType The type of the file.
+ * @throws Exception if an error occurs.
+ */
+ protected void handleCreate(final FileType newType) throws Exception
+ {
+ synchronized (fs)
{
- throw new FileSystemException("vfs.provider/write-append-not-supported.error", fileName);
+ if (attached)
+ {
+ // Fix up state
+ injectType(newType);
+
+ removeChildrenCache();
+ // children = EMPTY_FILE_ARRAY;
+
+ // Notify subclass
+ onChange();
+ }
+
+ // Notify parent that its child list may no longer be valid
+ notifyParent(this.getName(), newType);
+
+ // Notify the file system
+ fs.fireFileCreated(this);
}
+ }
- if (getType() == FileType.IMAGINARY)
+ /**
+ * Called when this file is deleted. Updates cached info and notifies
+ * subclasses, parent and file system.
+ * @throws Exception if an error occurs.
+ */
+ protected void handleDelete() throws Exception
+ {
+ synchronized (fs)
{
[... 760 lines stripped ...]