diff options
Diffstat (limited to 'org.openembedded.bc.ui/src/org/openembedded/bc/ui/filesystem/OEFile.java')
-rw-r--r-- | org.openembedded.bc.ui/src/org/openembedded/bc/ui/filesystem/OEFile.java | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/org.openembedded.bc.ui/src/org/openembedded/bc/ui/filesystem/OEFile.java b/org.openembedded.bc.ui/src/org/openembedded/bc/ui/filesystem/OEFile.java new file mode 100644 index 0000000..673bb56 --- /dev/null +++ b/org.openembedded.bc.ui/src/org/openembedded/bc/ui/filesystem/OEFile.java @@ -0,0 +1,465 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Ken Gilmer - adaptation from internal class. + *******************************************************************************/ +package org.openembedded.bc.ui.filesystem; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.List; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileInfo; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.filesystem.IFileSystem; +import org.eclipse.core.filesystem.URIUtil; +import org.eclipse.core.filesystem.provider.FileInfo; +import org.eclipse.core.filesystem.provider.FileStore; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +/** + * File system implementation based on storage of files in the local + * operating system's file system. + */ +public class OEFile extends FileStore { + private static int attributes(File aFile) { + if (!aFile.exists() || aFile.canWrite()) + return EFS.NONE; + return EFS.ATTRIBUTE_READ_ONLY; + } + + /** + * The java.io.File that this store represents. + */ + protected final File file; + private List ignorePaths; + + /** + * The absolute file system path of the file represented by this store. + */ + protected final String filePath; + + private final String root; + + /** + * Creates a new local file. + * + * @param file The file this local file represents + * @param root + */ + public OEFile(File file, List ignorePaths, String root) { + this.file = file; + this.ignorePaths = ignorePaths; + this.root = root; + this.filePath = file.getAbsolutePath(); + } + + /** + * This method is called after a failure to modify a file or directory. + * Check to see if the parent is read-only and if so then + * throw an exception with a more specific message and error code. + * + * @param target The file that we failed to modify + * @param exception The low level exception that occurred, or <code>null</code> + * @throws CoreException A more specific exception if the parent is read-only + */ + private void checkReadOnlyParent(File target, Throwable exception) throws CoreException { + File parent = target.getParentFile(); + if (parent != null && (attributes(parent) & EFS.ATTRIBUTE_READ_ONLY) != 0) { + String message = NLS.bind(Messages.readOnlyParent, target.getAbsolutePath()); + Policy.error(EFS.ERROR_PARENT_READ_ONLY, message, exception); + } + } + + @Override + public String[] childNames(int options, IProgressMonitor monitor) { + String[] names = file.list(); + return (names == null ? EMPTY_STRING_ARRAY : names); + } + + @Override + public IFileStore[] childStores(int options, IProgressMonitor monitor) throws CoreException { + String[] children = childNames(options, monitor); + IFileStore[] wrapped = new IFileStore[children.length]; + + for (int i = 0; i < wrapped.length; i++) { + String fullPath = file.toString() +File.separatorChar + children[i]; + + if (ignorePaths.contains(fullPath)) { + wrapped[i] = getDeadChild(children[i]); + } else { + wrapped[i] = getChild(children[i]); + } + } + + return wrapped; + } + + @Override + public void copy(IFileStore destFile, int options, IProgressMonitor monitor) throws CoreException { + if (destFile instanceof OEFile) { + File source = file; + File destination = ((OEFile) destFile).file; + //handle case variants on a case-insensitive OS, or copying between + //two equivalent files in an environment that supports symbolic links. + //in these nothing needs to be copied (and doing so would likely lose data) + try { + if (source.getCanonicalFile().equals(destination.getCanonicalFile())) { + //nothing to do + return; + } + } catch (IOException e) { + String message = NLS.bind(Messages.couldNotRead, source.getAbsolutePath()); + Policy.error(EFS.ERROR_READ, message, e); + } + } + //fall through to super implementation + super.copy(destFile, options, monitor); + } + + @Override + public void delete(int options, IProgressMonitor monitor) throws CoreException { + if (monitor == null) + monitor = new NullProgressMonitor(); + else + monitor = new NullProgressMonitor(); + try { + monitor.beginTask(NLS.bind(Messages.deleting, this), 200); + String message = Messages.deleteProblem; + MultiStatus result = new MultiStatus(Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, null); + + //don't allow Eclipse to delete entire OE directory + + if (!isProject()) { + internalDelete(file, filePath, result, monitor); + } + + if (!result.isOK()) + throw new CoreException(result); + } finally { + monitor.done(); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof OEFile)) + return false; + + OEFile otherFile = (OEFile) obj; + + return file.equals(otherFile.file); + } + + @Override + public IFileInfo fetchInfo(int options, IProgressMonitor monitor) { + //in-lined non-native implementation + FileInfo info = new FileInfo(file.getName()); + final long lastModified = file.lastModified(); + if (lastModified <= 0) { + //if the file doesn't exist, all other attributes should be default values + info.setExists(false); + return info; + } + info.setLastModified(lastModified); + info.setExists(true); + info.setLength(file.length()); + info.setDirectory(file.isDirectory()); + info.setAttribute(EFS.ATTRIBUTE_READ_ONLY, file.exists() && !file.canWrite()); + info.setAttribute(EFS.ATTRIBUTE_HIDDEN, file.isHidden()); + return info; + } + + @Override + public IFileStore getChild(IPath path) { + return new OEFile(new File(file, path.toOSString()), ignorePaths, root); + } + + @Override + public IFileStore getChild(String name) { + return new OEFile(new File(file, name), ignorePaths, root); + } + + private IFileStore getDeadChild(String name) { + return new OEIgnoreFile(new File(file, name)); + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.filesystem.IFileStore#getFileSystem() + */ + @Override + public IFileSystem getFileSystem() { + return OEFileSystem.getInstance(); + } + + @Override + public String getName() { + return file.getName(); + } + + @Override + public IFileStore getParent() { + File parent = file.getParentFile(); + return parent == null ? null : new OEFile(parent, ignorePaths, root); + } + + @Override + public int hashCode() { + return file.hashCode(); + } + + /** + * Deletes the given file recursively, adding failure info to + * the provided status object. The filePath is passed as a parameter + * to optimize java.io.File object creation. + */ + private boolean internalDelete(File target, String pathToDelete, MultiStatus status, IProgressMonitor monitor) { + //first try to delete - this should succeed for files and symbolic links to directories + if (target.delete() || !target.exists()) + return true; + if (target.isDirectory()) { + monitor.subTask(NLS.bind(Messages.deleting, target)); + String[] list = target.list(); + if (list == null) + list = EMPTY_STRING_ARRAY; + int parentLength = pathToDelete.length(); + boolean failedRecursive = false; + for (int i = 0, imax = list.length; i < imax; i++) { + //optimized creation of child path object + StringBuffer childBuffer = new StringBuffer(parentLength + list[i].length() + 1); + childBuffer.append(pathToDelete); + childBuffer.append(File.separatorChar); + childBuffer.append(list[i]); + String childName = childBuffer.toString(); + // try best effort on all children so put logical OR at end + failedRecursive = !internalDelete(new java.io.File(childName), childName, status, monitor) || failedRecursive; + monitor.worked(1); + } + try { + // don't try to delete the root if one of the children failed + if (!failedRecursive && target.delete()) + return true; + } catch (Exception e) { + // we caught a runtime exception so log it + String message = NLS.bind(Messages.couldnotDelete, target.getAbsolutePath()); + status.add(new Status(IStatus.ERROR, Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, e)); + return false; + } + } + //if we got this far, we failed + String message = null; + if (fetchInfo().getAttribute(EFS.ATTRIBUTE_READ_ONLY)) + message = NLS.bind(Messages.couldnotDeleteReadOnly, target.getAbsolutePath()); + else + message = NLS.bind(Messages.couldnotDelete, target.getAbsolutePath()); + status.add(new Status(IStatus.ERROR, Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, null)); + return false; + } + + @Override + public boolean isParentOf(IFileStore other) { + if (!(other instanceof OEFile)) + return false; + String thisPath = filePath; + String thatPath = ((OEFile) other).filePath; + int thisLength = thisPath.length(); + int thatLength = thatPath.length(); + //if equal then not a parent + if (thisLength >= thatLength) + return false; + if (getFileSystem().isCaseSensitive()) { + if (thatPath.indexOf(thisPath) != 0) + return false; + } else { + if (thatPath.toLowerCase().indexOf(thisPath.toLowerCase()) != 0) + return false; + } + //The common portion must end with a separator character for this to be a parent of that + return thisPath.charAt(thisLength - 1) == File.separatorChar || thatPath.charAt(thisLength) == File.separatorChar; + } + + /** + * @return + */ + private boolean isProject() { + return this.file.toString().equals(root); + } + + @Override + public IFileStore mkdir(int options, IProgressMonitor monitor) throws CoreException { + boolean shallow = (options & EFS.SHALLOW) != 0; + //must be a directory + if (shallow) + file.mkdir(); + else + file.mkdirs(); + if (!file.isDirectory()) { + checkReadOnlyParent(file, null); + String message = NLS.bind(Messages.failedCreateWrongType, filePath); + Policy.error(EFS.ERROR_WRONG_TYPE, message); + } + return this; + } + + @Override + public void move(IFileStore destFile, int options, IProgressMonitor monitor) throws CoreException { + if (!(destFile instanceof OEFile)) { + super.move(destFile, options, monitor); + return; + } + File source = file; + File destination = ((OEFile) destFile).file; + boolean overwrite = (options & EFS.OVERWRITE) != 0; + monitor = Policy.monitorFor(monitor); + try { + monitor.beginTask(NLS.bind(Messages.moving, source.getAbsolutePath()), 10); + //this flag captures case renaming on a case-insensitive OS, or moving + //two equivalent files in an environment that supports symbolic links. + //in these cases we NEVER want to delete anything + boolean sourceEqualsDest = false; + try { + sourceEqualsDest = source.getCanonicalFile().equals(destination.getCanonicalFile()); + } catch (IOException e) { + String message = NLS.bind(Messages.couldNotMove, source.getAbsolutePath()); + Policy.error(EFS.ERROR_WRITE, message, e); + } + if (!sourceEqualsDest && !overwrite && destination.exists()) { + String message = NLS.bind(Messages.fileExists, destination.getAbsolutePath()); + Policy.error(EFS.ERROR_EXISTS, message); + } + if (source.renameTo(destination)) { + // double-check to ensure we really did move + // since java.io.File#renameTo sometimes lies + if (!sourceEqualsDest && source.exists()) { + // XXX: document when this occurs + if (destination.exists()) { + // couldn't delete the source so remove the destination and throw an error + // XXX: if we fail deleting the destination, the destination (root) may still exist + new OEFile(destination, ignorePaths, root).delete(EFS.NONE, null); + String message = NLS.bind(Messages.couldnotDelete, source.getAbsolutePath()); + Policy.error(EFS.ERROR_DELETE, message); + } + // source exists but destination doesn't so try to copy below + } else { + if (!destination.exists()) { + // neither the source nor the destination exist. this is REALLY bad + String message = NLS.bind(Messages.failedMove, source.getAbsolutePath(), destination.getAbsolutePath()); + Policy.error(EFS.ERROR_WRITE, message); + } + //the move was successful + monitor.worked(10); + return; + } + } + // for some reason renameTo didn't work + if (sourceEqualsDest) { + String message = NLS.bind(Messages.couldNotMove, source.getAbsolutePath()); + Policy.error(EFS.ERROR_WRITE, message, null); + } + // fall back to default implementation + super.move(destFile, options, Policy.subMonitorFor(monitor, 10)); + } finally { + monitor.done(); + } + } + + @Override + public InputStream openInputStream(int options, IProgressMonitor monitor) throws CoreException { + monitor = Policy.monitorFor(monitor); + try { + monitor.beginTask("", 1); //$NON-NLS-1$ + return new FileInputStream(file); + } catch (FileNotFoundException e) { + String message; + if (!file.exists()) + message = NLS.bind(Messages.fileNotFound, filePath); + else if (file.isDirectory()) + message = NLS.bind(Messages.notAFile, filePath); + else + message = NLS.bind(Messages.couldNotRead, filePath); + Policy.error(EFS.ERROR_READ, message, e); + return null; + } finally { + monitor.done(); + } + } + + @Override + public OutputStream openOutputStream(int options, IProgressMonitor monitor) throws CoreException { + monitor = Policy.monitorFor(monitor); + try { + monitor.beginTask("", 1); //$NON-NLS-1$ + return new FileOutputStream(file, (options & EFS.APPEND) != 0); + } catch (FileNotFoundException e) { + checkReadOnlyParent(file, e); + String message; + String path = filePath; + if (file.isDirectory()) + message = NLS.bind(Messages.notAFile, path); + else + message = NLS.bind(Messages.couldNotWrite, path); + Policy.error(EFS.ERROR_WRITE, message, e); + return null; + } finally { + monitor.done(); + } + } + + @Override + public void putInfo(IFileInfo info, int options, IProgressMonitor monitor) throws CoreException { + boolean success = true; + + //native does not currently set last modified + if ((options & EFS.SET_LAST_MODIFIED) != 0) + success &= file.setLastModified(info.getLastModified()); + if (!success && !file.exists()) + Policy.error(EFS.ERROR_NOT_EXISTS, NLS.bind(Messages.fileNotFound, filePath)); + } + + /* (non-Javadoc) + * @see org.eclipse.core.filesystem.provider.FileStore#toLocalFile(int, org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public File toLocalFile(int options, IProgressMonitor monitor) throws CoreException { + if (options == EFS.CACHE) + return super.toLocalFile(options, monitor); + return file; + } + + /* (non-Javadoc) + * @see org.eclipse.core.filesystem.IFileStore#toString() + */ + @Override + public String toString() { + return file.toString(); + } + + /* (non-Javadoc) + * @see org.eclipse.core.filesystem.IFileStore#toURI() + */ + @Override + public URI toURI() { + return URIUtil.toURI(filePath); + } +} |