/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.filesystems;
import java.beans.PropertyVetoException;
import java.io.*;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.util.NbBundle;
/** Local filesystem. Provides access to files on local disk.
* <p>For historical reasons many AbstractFileSystem.* methods are implemented
* as protected in this class. Do not call them! Subclasses might override
* them, or (better) use delegation.
*/
public class LocalFileSystem extends AbstractFileSystem {
/** generated Serialized Version UID */
private static final long serialVersionUID = -5355566113542272442L;
/** Controlls the LocalFileSystem's automatic refresh.
* If the refresh time interval is set from the System.property, than this value is used.
* Otherwise, the refresh time interval is set to 0 which means the refresh
* is disabled. */
private static final int REFRESH_TIME = Integer.getInteger
("org.openide.filesystems.LocalFileSystem.REFRESH_TIME",0).intValue(); // NOI18N
/** root file */
private File rootFile = new File ("."); // NOI18N
/** is read only */
private boolean readOnly;
/** Constructor.
*/
public LocalFileSystem () {
Impl impl = new Impl (this);
info = impl;
change = impl;
DefaultAttributes a = new InnerAttrs (this,info, change, impl);
attr = a;
list = a;
setRefreshTime (REFRESH_TIME);
}
/** Constructor. Allows user to provide own capabilities
* for this filesystem.
* @param cap capabilities for this filesystem
*/
public LocalFileSystem (FileSystemCapability cap) {
this ();
setCapability (cap);
}
/* Human presentable name */
public String getDisplayName() {
boolean validValue = (isValid() && rootFile != null &&
rootFile.isDirectory() && rootFile.exists ());
if(validValue)
return getString ("LAB_FileSystemValid", rootFile.getAbsolutePath ());
else
return getString("LAB_FileSystemInvalid", rootFile.getAbsolutePath ());
}
/** Set the root directory of the filesystem.
* @param r file to set root to
* @exception PropertyVetoException if the value if vetoed by someone else (usually
* by the {@link org.openide.filesystems.Repository Repository})
* @exception IOException if the root does not exists or some other error occured
*/
public synchronized void setRootDirectory (File r) throws PropertyVetoException, IOException {
if (!r.exists() || r.isFile ()) {
FSException.io ("EXC_RootNotExist", r.getAbsolutePath ()); // NOI18N
}
String oldDisplayName = getDisplayName ();
setSystemName(computeSystemName (r));
rootFile = r;
firePropertyChange(PROP_ROOT, null, refreshRoot ());
firePropertyChange (PROP_DISPLAY_NAME, oldDisplayName, getDisplayName ());
}
/** Get the root directory of the filesystem.
* @return root directory
*/
public File getRootDirectory () {
return rootFile;
}
/** Set whether the filesystem should be read only.
* @param flag <code>true</code> if it should
*/
public void setReadOnly(boolean flag) {
if (flag != readOnly) {
readOnly = flag;
firePropertyChange (PROP_READ_ONLY,
!flag ? Boolean.TRUE : Boolean.FALSE,
flag ? Boolean.TRUE : Boolean.FALSE);
}
}
/* Test whether filesystem is read only.
* @return <true> if filesystem is read only
*/
public boolean isReadOnly() {
return readOnly;
}
/** Prepare environment by adding the root directory of the filesystem to the class path.
* @param environment the environment to add to
*/
public void prepareEnvironment(FileSystem.Environment environment) {
environment.addClassPath(rootFile.getAbsolutePath ());
}
/** Compute the system name of this filesystem for a given root directory.
* <P>
* The default implementation simply returns the filename separated by slashes.
* @see FileSystem#setSystemName
* @param rootFile root directory for the filesystem
* @return system name for the filesystem
*/
protected String computeSystemName (File rootFile) {
return rootFile.getAbsolutePath ().replace(File.separatorChar, '/');
}
//
// List
//
protected String[] children (String name) {
File f = getFile (name);
if (f.isDirectory ()) {
return f.list ();
} else {
return null;
}
}
//
// Change
//
protected void createFolder (String name) throws java.io.IOException {
File f = getFile (name);
if (name.equals ("")) { // NOI18N
FSException.io ("EXC_CannotCreateF", new Object[] { f.getName (), getDisplayName (), f.getAbsolutePath ()}); // NOI18N
}
if (f.exists()) {
FSException.io ("EXC_FolderAlreadyExist", new Object[] { f.getName (), getDisplayName (), f.getAbsolutePath ()}); // NOI18N
}
boolean b = createRecursiveFolder(f);
if (!b) {
FSException.io ("EXC_CannotCreateF", new Object[] { f.getName (), getDisplayName (), f.getAbsolutePath ()}); // NOI18N
}
}
/*
* @return true if RefreshAction should be enabled
*/
boolean isEnabledRefreshFolder () {
return true;
}
/** Creates new folder and all necessary subfolders
* @param f folder to create
* @return <code>true</code> if the file exists when returning from this method
*/
private static boolean createRecursiveFolder(File f) {
if (f.exists()) return true;
if (!f.isAbsolute())
f = f.getAbsoluteFile();
String par = f.getParent();
if (par == null) return false;
if (!createRecursiveFolder(new File(par))) return false;
f.mkdir();
return f.exists();
}
protected void createData (String name) throws IOException {
File f = getFile (name);
if (!f.createNewFile ()) {
String msg = NbBundle.getMessage (
LocalFileSystem.class,
"EXC_DataAlreadyExist",
new Object[] { f.getName (), getDisplayName (), f.getAbsolutePath () }
);
SyncFailedException e = new SyncFailedException (msg);
ExternalUtil.annotate (e, msg);
throw e;
}
/* JST: Maybe handled by createNewFile, but probably
if (!tmp.exists())
throw new IOException(MessageFormat.format (LocalFileSystem.getString("EXC_CannotCreateD"), errorParams));
*/
}
protected void rename(String oldName, String newName) throws IOException {
File of = getFile (oldName);
File nf = getFile (newName);
// #7086 - (nf.exists() && !nf.equals(of)) instead of nf.exists() - fix for Win32
if ((nf.exists() && !nf.equals(of)) || !of.renameTo (nf)) {
FSException.io ("EXC_CannotRename", oldName, getDisplayName (), newName); // NOI18N
}
}
protected void delete(String name) throws IOException {
File file = getFile(name);
if (deleteFile(file) != SUCCESS) {
if (file.exists())
FSException.io("EXC_CannotDelete", name, getDisplayName(), file.getAbsolutePath()); // NOI18N
else {
/** When file externaly deleted and fo.delete () is called before
periodical refresh */
FileObject thisFo = findResource(name);
if (thisFo != null) {
if (thisFo.getParent() != null)
thisFo.getParent().refresh();
thisFo.refresh();
if (thisFo.isValid())
FSException.io("EXC_CannotDelete", name, getDisplayName(), file.getAbsolutePath()); // NOI18N
}
}
}
}
private static final int SUCCESS = 0;
private static final int FAILURE = 1;
private static final int NOT_EXISTS = 3;
/** Method that recursivelly deletes all files in a folder.
* @return true if successful
*/
private static int deleteFile (File file) {
boolean ret = file.delete();
if (ret) {
return SUCCESS;
}
if (! file.exists()) {
return NOT_EXISTS;
}
if (file.isDirectory()) {
// first of all delete whole content
File[] arr = file.listFiles();
for (int i = 0; i < arr.length; i++) {
if (deleteFile (arr[i]) != SUCCESS) {
return FAILURE;
}
}
}
// delete the file itself
return (file.delete() ? SUCCESS : FAILURE);
}
//
// Info
//
protected java.util.Date lastModified(String name) {
return new java.util.Date (getFile (name).lastModified ());
}
protected boolean folder (String name) {
return getFile (name).isDirectory ();
}
protected boolean readOnly (String name) {
File f = getFile (name);
return !f.canWrite () && f.exists ();
}
protected String mimeType (String name) {
return null;
}
protected long size (String name) {
return getFile (name).length ();
}
// ===============================================================================
// This part of code could be used for monitoring of closing file streams.
/* public static java.util.HashMap openedIS = new java.util.HashMap();
public static java.util.HashMap openedOS = new java.util.HashMap();
static class DebugIS extends FileInputStream {
public DebugIS(File f) throws java.io.FileNotFoundException { super(f); }
public void close() throws IOException { openedIS.remove(this); super.close(); }
};
static class DebugOS extends FileOutputStream {
public DebugOS(File f) throws java.io.IOException { super(f); }
public void close() throws IOException { openedOS.remove(this); super.close(); }
};
public InputStream inputStream (String name) throws java.io.FileNotFoundException {
DebugIS is = new DebugIS(getFile(name));
openedIS.put(is, new Exception());
return is;
}
public OutputStream outputStream (String name) throws java.io.IOException {
DebugOS os = new DebugOS(getFile(name));
openedOS.put(os, new Exception());
return os;
}*/
// End of the debug part
// ============================================================================
// Begin of the original part
protected InputStream inputStream (String name) throws java.io.FileNotFoundException {
FileInputStream fis;
File file = null;
try {
fis = new FileInputStream(file=getFile(name));
} catch(FileNotFoundException exc) {
if (file == null || !file.exists())
ExternalUtil.annotate (exc, NbBundle.getMessage(LocalFileSystem.class,"EXC_FileOutsideModified"));
throw exc;
}
return fis;
}
protected OutputStream outputStream (String name) throws java.io.IOException {
return new FileOutputStream (getFile (name));
}
// End of the original part
// ============================================================================
protected void lock (String name) throws IOException {
File file = getFile (name);
if ((!file.canWrite () && file.exists ()) ||
isReadOnly()) {
FSException.io ("EXC_CannotLock", name, getDisplayName (), file.getAbsolutePath ()); // NOI18N
}
}
protected void unlock (String name) {
}
protected void markUnimportant (String name) {
}
/** Creates file for given string name.
* @param name the name
* @return the file
*/
private File getFile (String name) {
// XXX should this be name.replace('/', File.separatorChar)? Cf. BT #4745638.
return new File (rootFile, name);
}
/**
* @param in the input stream to read from
* @exception IOException error during read
* @exception ClassNotFoundException when class not found
*/
private void readObject (java.io.ObjectInputStream in)
throws java.io.IOException, java.lang.ClassNotFoundException {
in.defaultReadObject ();
((ObjectInputStream)in).registerValidation(new ObjectInputValidation() {
public void validateObject() {
if (attr.getClass() == DefaultAttributes.class) {
Impl impl = new Impl(LocalFileSystem.this);
attr = new InnerAttrs(LocalFileSystem.this,impl, impl, impl);
}
}
}, 0);
}
/** The implementation class that implements List, Info
* and Change interfaces and delegates all the methods
* to appropriate methods of LocalFileSystem.
*/
public static class Impl extends Object
implements AbstractFileSystem.List, AbstractFileSystem.Info,
AbstractFileSystem.Change {
/** generated Serialized Version UID */
static final long serialVersionUID = -8432015909317698511L;
/** pointer to local filesystem */
private LocalFileSystem fs;
/** Pointer to local filesystem
* @param fs the filesystem this impl is connected to
*/
public Impl (LocalFileSystem fs) {
this.fs = fs;
}
/*
*
* Scans children for given name
*/
public String[] children (String name) {
return fs.children (name);
}
//
// Change
//
/*
* Creates new folder named name.
* @param name name of folder
* @throws IOException if operation fails
*/
public void createFolder (String name) throws java.io.IOException {
fs.createFolder (name);
}
/*
* Create new data file.
*
* @param name name of the file
*
* @return the new data file object
* @exception IOException if the file cannot be created (e.g. already exists)
*/
public void createData (String name) throws IOException {
fs.createData (name);
}
/*
* Renames a file.
*
* @param oldName old name of the file
* @param newName new name of the file
*/
public void rename(String oldName, String newName) throws IOException {
fs.rename (oldName, newName);
}
/*
* Delete the file.
*
* @param name name of file
* @exception IOException if the file could not be deleted
*/
public void delete (String name) throws IOException {
fs.delete (name);
}
//
// Info
//
/*
*
* Get last modification time.
* @param name the file to test
* @return the date
*/
public java.util.Date lastModified(String name) {
return fs.lastModified (name);
}
/*
* Test if the file is folder or contains data.
* @param name name of the file
* @return true if the file is folder, false otherwise
*/
public boolean folder (String name) {
return fs.folder (name);
}
/*
* Test whether this file can be written to or not.
* @param name the file to test
* @return <CODE>true</CODE> if file is read-only
*/
public boolean readOnly (String name) {
return fs.readOnly (name);
}
/*
* Get the MIME type of the file.
* Uses {@link FileUtil#getMIMEType}.
*
* @param name the file to test
* @return the MIME type textual representation, e.g. <code>"text/plain"</code>
*/
public String mimeType (String name) {
return fs.mimeType (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) {
return fs.size (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 exists or is invalid
*/
public InputStream inputStream (String name) throws java.io.FileNotFoundException {
return fs.inputStream (name);
}
/*
* Get output stream.
*
* @param name the file to test
* @return output stream to overwrite the contents of this file
* @exception IOException if an error occures (the file is invalid, etc.)
*/
public OutputStream outputStream (String name) throws java.io.IOException {
return fs.outputStream (name);
}
/*
* Does nothing to lock the file.
*
* @param name name of the file
*/
public void lock (String name) throws IOException {
fs.lock (name);
}
/*
* Does nothing to unlock the file.
*
* @param name name of the file
*/
public void unlock (String name) {
fs.unlock (name);
}
/*
* Does nothing to mark the file as unimportant.
*
* @param name the file to mark
*/
public void markUnimportant (String name) {
fs.markUnimportant (name);
}
}
/** This class adds new virtual attribute "java.io.File".
* Because of the fact that FileObjects of LocalFileSystem are convertable
* to java.io.File by means of attributes. */
private static class InnerAttrs extends DefaultAttributes {
static final long serialVersionUID = 1257351369229921993L;
LocalFileSystem lfs;
public InnerAttrs(LocalFileSystem lfs, AbstractFileSystem.Info info,
AbstractFileSystem.Change change,AbstractFileSystem.List list ) {
super (info, change, list);
this.lfs = lfs;
}
public Object readAttribute(String name, String attrName) {
if (attrName.equals("java.io.File")) // NOI18N
return lfs.getFile(name);
return super.readAttribute(name, attrName);
}
}
}