use of com.mucommander.commons.file.AbstractFile in project mucommander by mucommander.
the class PathUtils method resolveDestination.
/**
* Resolves a destination path entered by the user and returns a {@link ResolvedDestination} object that
* that contains a {@link AbstractFile} instance corresponding to the path and a type that describes the kind of
* destination that was resolved. <code>null</code> is returned if the path is not a valid destination (see below)
* or could not be resolved, for example becuase of I/O or authentication error.
* <p>
* The given path may be either absolute or relative to the specified base folder. If the base folder argument is
* <code>null</code> and the path is relative, <code>null</code> will always be returned.
* The path may contain '.', '..' and '~' tokens which will be left for the corresponding
* {@link com.mucommander.commons.file.SchemeParser} to canonize.
* </p>
* <p>
* The path may refer to the following listed destination types. In all cases, the destination's parent folder must
* exist, if it doesn't <code>null</code> will always be returned. For example, <code>/non_existing_folder/file</code>
* is not a valid destination (provided that '/non_existing_folder' does not exist).
* <dl>
* <dt>{@link ResolvedDestination#EXISTING_FOLDER}</dt><dd>if the path denotes a folder, either a directory or a
* browsable archive.</dd>
* <dt>{@link ResolvedDestination#EXISTING_FILE}</dt><dd>if the path denotes a regular file. The file may be a browsable archive,
* see below.</dd>
* <dt>{@link ResolvedDestination#NEW_FILE}</dt><dd>if the path denotes a non-existing file whose parent exists.</dd>
* </dl>
* Paths to browsable archives are considered as denoting a folder only if they end with a trailing separator
* character. If they don't, they're considered as denoting a regular file. For example,
* <code>/existing_folder/existing_archive.zip/</code> refers to the archive as a folder where as
* <code>/existing_folder/existing_archive.zip</code> refers to the archive as a regular file.
* </p>
*
* @param destPath the destination path to resolve
* @param baseFolder the base folder used for relative paths, <code>null</code> to accept only absolute paths
* @param parent the parent of destPath
* @return the object that that contains a {@link AbstractFile} instance corresponding to the path and a type that
* describes the kind of destination that was resolved
*/
public static ResolvedDestination resolveDestination(String destPath, AbstractFile baseFolder, AbstractFile parent) {
FileURL destURL;
// Try to resolve the path as a URL
try {
destURL = FileURL.getFileURL(destPath);
// destPath is absolute
} catch (MalformedURLException e) {
// Abort now if there is no base folder
if (baseFolder == null)
return null;
String separator = baseFolder.getSeparator();
// Start by cloning the base folder's URL, including credentials and properties
FileURL baseFolderURL = baseFolder.getURL();
destURL = (FileURL) baseFolderURL.clone();
String basePath = destURL.getPath();
if (!destPath.equals(""))
destURL.setPath(basePath + (basePath.endsWith(separator) ? "" : separator) + destPath);
// => parse the URL from scratch to have the SchemeParser canonize them.
try {
destURL = FileURL.getFileURL(destURL.toString(false));
// Import credentials separately, so that login and passwords that contain URI-unsafe characters
// such as '/' are properly parsed.
destURL.setCredentials(baseFolderURL.getCredentials());
destURL.importProperties(baseFolderURL);
} catch (MalformedURLException e2) {
return null;
}
}
// No point in going any further if the URL cannot be resolved into a file
AbstractFile destFile = FileFactory.getFile(destURL);
if (destFile == null) {
LOGGER.info("could not resolve a file for {}", destURL);
return null;
}
// Test if the destination file exists
boolean destFileExists = destFile.exists();
if (destFileExists) {
// if they don't, they'll refer to the archive as a file.
if (destFile.isDirectory() || (destPath.endsWith(destFile.getSeparator()) && destFile.isBrowsable()))
return new ResolvedDestination(destFile, DestinationType.EXISTING_FOLDER, destFile);
}
if (Objects.equals(parent, destFile.getParent())) {
destFile.setParent(parent);
}
// Test if the destination's parent exists, if not the path is not a valid destination
AbstractFile destParent = destFile.getParent();
if (destParent == null || !destParent.exists())
return null;
return new ResolvedDestination(destFile, destFileExists ? DestinationType.EXISTING_FILE : DestinationType.NEW_FILE, destParent);
}
use of com.mucommander.commons.file.AbstractFile in project mucommander by mucommander.
the class LocalFile method getVolumes.
/**
* Resolves and returns all local volumes:
* <ul>
* <li>On UNIX-based OSes, these are the mount points declared in <code>/etc/ftab</code>.</li>
* <li>On the Windows platform, these are the drives displayed in Explorer. Some of the returned volumes may
* correspond to removable drives and thus may not always be available -- if they aren't, {@link #exists()} will
* return <code>false</code>.</li>
* </ul>
* <p>
* The return list of volumes is purposively not cached so that new volumes will be returned as soon as they are
* mounted.
* </p>
*
* @return all local volumes
*/
public static AbstractFile[] getVolumes() {
Vector<AbstractFile> volumesV = new Vector<AbstractFile>();
// (like 'Hard drive' or whatever silly name the user gave his primary hard disk) to /
if (OsFamily.MAC_OS.isCurrent()) {
addMacOSXVolumes(volumesV);
} else {
// Add java.io.File's root folders
addJavaIoFileRoots(volumesV);
// Add /proc/mounts folders under UNIX-based systems.
if (OsFamily.getCurrent().isUnixBased())
addMountEntries(volumesV);
}
// Add home folder, if it is not already present in the list
AbstractFile homeFolder = getUserHome();
if (!(homeFolder == null || volumesV.contains(homeFolder)))
volumesV.add(homeFolder);
addDesktopEntry(volumesV, homeFolder);
AbstractFile[] volumes = new AbstractFile[volumesV.size()];
volumesV.toArray(volumes);
return volumes;
}
use of com.mucommander.commons.file.AbstractFile in project mucommander by mucommander.
the class LocalFile method ls.
@Override
public AbstractFile[] ls(FilenameFilter filenameFilter) throws IOException {
File[] files = file.listFiles(filenameFilter == null ? null : new LocalFilenameFilter(filenameFilter));
if (files == null)
throw new IOException();
int nbFiles = files.length;
AbstractFile[] children = new AbstractFile[nbFiles];
for (int i = 0; i < nbFiles; i++) {
// Clone the FileURL of this file and set the child's path, this is more efficient than creating a new
// FileURL instance from scratch.
FileURL childURL = (FileURL) fileURL.clone();
childURL.setPath(absPath + SEPARATOR + files[i].getName());
// Retrieves an AbstractFile (LocalFile or AbstractArchiveFile) instance that's potentially already in
// the cache, reuse this file as the file's parent, and the already-created java.io.File instance.
children[i] = FileFactory.getFile(childURL, this, Collections.singletonMap("createdFile", files[i]));
}
return children;
}
use of com.mucommander.commons.file.AbstractFile in project mucommander by mucommander.
the class LocalFile method getVolume.
/**
* Overridden to return the local volume on which this file is located. The returned volume is one of the volumes
* returned by {@link #getVolumes()}.
*/
@Override
public AbstractFile getVolume() {
AbstractFile[] volumes = LocalFile.getVolumes();
// Looks for the volume that best matches this file, i.e. the volume that is the deepest parent of this file.
// If this file is itself a volume, return it.
int bestDepth = -1;
AbstractFile bestMatch = null;
String thisPath = getAbsolutePath(true);
for (AbstractFile volume : volumes) {
String volumePath = volume.getAbsolutePath(true);
if (thisPath.equals(volumePath)) {
return this;
} else if (thisPath.startsWith(volumePath)) {
int depth = PathUtils.getDepth(volumePath, volume.getSeparator());
if (depth > bestDepth) {
bestDepth = depth;
bestMatch = volume;
}
}
}
if (bestMatch != null)
return bestMatch;
// If no volume matched this file (shouldn't normally happen), return the root folder
return getRoot();
}
use of com.mucommander.commons.file.AbstractFile in project mucommander by mucommander.
the class UNCFile method renameTo.
@Override
public void renameTo(AbstractFile destFile) throws IOException, UnsupportedFileOperationException {
// Throw an exception if the file cannot be renamed to the specified destination.
// Fail in some situations where java.io.File#renameTo() doesn't.
// Note that java.io.File#renameTo()'s implementation is system-dependant, so it's always a good idea to
// perform all those checks even if some are not necessary on this or that platform.
checkRenamePrerequisites(destFile, true, false);
// The behavior of java.io.File#renameTo() when the destination file already exists is not consistent
// across platforms:
// - Under UNIX, it succeeds and return true
// - Under Windows, it fails and return false
// This ticket goes in great details about the issue: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4017593
//
// => Since this method is required to succeed when the destination file exists, the Windows platform needs
// special treatment.
destFile = destFile.getTopAncestor();
File destJavaIoFile = ((UNCFile) destFile).file;
// Note that Windows UNC paths are handled by checkRenamePrerequisites() when comparing hosts for equality.
if (!getRoot().equals(destFile.getRoot()))
throw new IOException();
if (Kernel32.isAvailable()) {
// call #exists() on the destination file which has a cost.
if (!Kernel32.getInstance().MoveFileEx(absPath, destFile.getAbsolutePath(), Kernel32API.MOVEFILE_REPLACE_EXISTING | Kernel32API.MOVEFILE_WRITE_THROUGH)) {
String errorMessage = Integer.toString(Kernel32.getInstance().GetLastError());
// TODO: use Kernel32.FormatMessage
throw new IOException("Rename using Kernel32 API failed: " + errorMessage);
} else {
// move successful
return;
}
}
if (!file.renameTo(destJavaIoFile))
throw new IOException();
}
Aggregations