/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * muCommander is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.bookmark.file; import com.mucommander.bookmark.Bookmark; import com.mucommander.bookmark.BookmarkBuilder; import com.mucommander.bookmark.BookmarkManager; import com.mucommander.commons.file.*; import com.mucommander.commons.file.protocol.ProtocolFile; import com.mucommander.commons.io.FileTransferException; import com.mucommander.commons.io.RandomAccessInputStream; import com.mucommander.commons.io.RandomAccessOutputStream; import java.io.*; /** * Represents a file in the <code>bookmark://</code> file system. * @author Nicolas Rinaudo */ public class BookmarkFile extends ProtocolFile { // - Instance fields ------------------------------------------------------- // ------------------------------------------------------------------------- /** Bookmark wrapped by this abstract file. */ private Bookmark bookmark; /** Underlying abstract file. */ private AbstractFile file; /** Permissions for all bookmark files: rw- (600 octal). Only the 'user' permissions bits are supported. */ final static FilePermissions PERMISSIONS = new SimpleFilePermissions(384, 448); // - Initialisation -------------------------------------------------------- // ------------------------------------------------------------------------- /** * Creates a new bookmark file wrapping the specified bookmark. * @param bookmark bookmark to wrap. * @throws IOException if the specified bookmark's URL cannot be resolved. */ protected BookmarkFile(Bookmark bookmark) throws IOException { super(FileURL.getFileURL(BookmarkProtocolProvider.BOOKMARK + "://" + java.net.URLEncoder.encode(bookmark.getName(), "UTF-8"))); this.bookmark = bookmark; } // - Helper methods -------------------------------------------------------- // ------------------------------------------------------------------------- /** * Returns the <code>AbstractFile</code> this instance wraps. * <p> * Some methods need to have access to the underlying file. This, however, requires * resolving the path which can be time consuming. Using this method ensures that the * path is only resolved if necessary, and at most once. * </p> * @return the <code>AbstractFile</code> this instance wraps. */ private synchronized AbstractFile getUnderlyingFile() { // Resolves the file if necessary. if(file == null) file = FileFactory.getFile(bookmark.getLocation()); return file; } /** * Returns the underlying bookmark. * @return the underlying bookmark. */ public Bookmark getBookmark() {return bookmark;} // - AbstractFile methods -------------------------------------------------- // ------------------------------------------------------------------------- /** * Returns the underlying bookmark's name. * @return the underlying bookmark's name. */ @Override public String getName() {return bookmark.getName();} /** * Returns the wrapped file's descendants. * @return the wrapped file's descendants. * @throws IOException if an I/O error occurs. * @throws UnsupportedFileOperationException if this operation is not supported by the underlying filesystem, * or is not implemented. */ @Override public AbstractFile[] ls() throws IOException, UnsupportedFileOperationException { return getUnderlyingFile().ls(); } /** * Returns the wrapped file's parent. * @return the wrapped file's parent. * @see #setParent(AbstractFile) */ @Override public AbstractFile getParent() { try { return new BookmarkRoot(); } catch(IOException e) { return null; } } /** * Returns the result of the wrapped file's <code>getFreeSpace()</code> methods. * @return the result of the wrapped file's <code>getFreeSpace()</code> methods. * @throws IOException if an I/O error occurred * @throws UnsupportedFileOperationException if this operation is not supported by the underlying filesystem, * or is not implemented. */ @Override public long getFreeSpace() throws IOException, UnsupportedFileOperationException {return getUnderlyingFile().getFreeSpace();} /** * Returns the result of the wrapped file's <code>getTotalSpace()</code> methods. * @return the result of the wrapped file's <code>getTotalSpace()</code> methods. * @throws IOException if an I/O error occurred * @throws UnsupportedFileOperationException if this operation is not supported by the underlying filesystem, * or is not implemented. */ @Override public long getTotalSpace() throws IOException, UnsupportedFileOperationException {return getUnderlyingFile().getTotalSpace();} /** * Returns <code>false</code>. * @return <code>false</code>. */ @Override public boolean isDirectory() {return true;} /** * Sets the wrapped file's parent. * @param parent object to use as the wrapped file's parent. * @see AbstractFile#getParent() */ @Override public void setParent(AbstractFile parent) { getUnderlyingFile().setParent(parent);} /** * Returns <code>true</code> if the specified bookmark exists. * <p> * A bookmark is said to exist if and only if it is known to the {@link com.mucommander.bookmark.BookmarkManager}. * </p> * @return <code>true</code> if the specified bookmark exists, <code>false</code> otherwise. */ @Override public boolean exists() {return BookmarkManager.getBookmark(bookmark.getName()) != null;} @Override public void mkfile() {BookmarkManager.addBookmark(bookmark);} public boolean equals(Object o) { // Makes sure we're working with an abstract file. if(!(o instanceof AbstractFile)) return false; // Retrieves the actual file instance. // We might have received a Proxied or Cached file, so we need to make sure // we 'unwrap' that before comparing. AbstractFile file = ((AbstractFile)o).getAncestor(); // We only know how to compare one bookmark file to the other. if(file instanceof BookmarkFile) return bookmark.equals(((BookmarkFile)file).getBookmark()); return false; } @Override public String getCanonicalPath() {return bookmark.getLocation();} // - Bookmark renaming ----------------------------------------------------- // ------------------------------------------------------------------------- /** * Attempts to rename the bookmark to the specified destination. * The operation will only be carried out if the specified destination is a <code>BookmarkFile</code> or has an * ancestor that is. * * @param destination where to move the bookmark to. * @throws IOException if the operation could not be carried out. */ @Override public void renameTo(AbstractFile destination) throws IOException { checkRenamePrerequisites(destination, true, true); Bookmark oldBookmark; Bookmark newBookmark; destination = destination.getTopAncestor(); // Makes sure we're working with a bookmark. if(!(destination instanceof BookmarkFile)) throw new IOException(); // Creates the new bookmark and checks for conflicts. newBookmark = new Bookmark(destination.getName(), bookmark.getLocation()); if((oldBookmark = BookmarkManager.getBookmark(newBookmark.getName())) != null) BookmarkManager.removeBookmark(oldBookmark); // Adds the new bookmark and deletes its 'old' version. BookmarkManager.addBookmark(newBookmark); BookmarkManager.removeBookmark(bookmark); } // TODO: bookmark deleting is currently disabled as a quick fix for #329 // /** // * Deletes the bookmark. // * <p> // * Deleting a bookmark means unregistering it from the {@link com.mucommander.bookmark.BookmarkManager}. // * </p> // */ // @Override // public void delete() { // BookmarkManager.removeBookmark(bookmark); // } @Override @UnsupportedFileOperation public void delete() throws UnsupportedFileOperationException { throw new UnsupportedFileOperationException(FileOperation.DELETE); } // - Bookmark duplication -------------------------------------------------- // ------------------------------------------------------------------------- /** * Tries to copy the bookmark to the specified destination. * <p> * If the specified destination is an instance of <code>BookmarkFile</code>, * this will duplicate the bookmark. Otherwise, this method will fail. * </p> * @param destination where to copy the bookmark to. * @throws FileTransferException if the specified destination is not an instance of <code>BookmarkFile</code>. */ @Override public void copyRemotelyTo(AbstractFile destination) throws IOException { // Makes sure we're working with a bookmark. destination = destination.getTopAncestor(); if(!(destination instanceof BookmarkFile)) throw new IOException(); // Copies this bookmark to the specified destination. BookmarkManager.addBookmark(new Bookmark(destination.getName(), bookmark.getLocation())); } // - Permissions ----------------------------------------------------------- // ------------------------------------------------------------------------- /** * Returns the same permissions for all boookmark files: rw- (600 octal). * Only the 'user' permissions bits are supported. * @return this file's permissions. * @see #changePermission(int,int,boolean) */ @Override public FilePermissions getPermissions() {return PERMISSIONS;} /** * Always throws an {@link UnsupportedFileOperationException} when called: bookmarks always have all permissions, * this is not changeable. * * @param access ignored. * @param permission ignored. * @param enabled ignored. * @see #getPermissions() */ @Override @UnsupportedFileOperation public void changePermission(PermissionAccess access, PermissionType permission, boolean enabled) throws UnsupportedFileOperationException { throw new UnsupportedFileOperationException(FileOperation.CHANGE_PERMISSION); } // - Import / export ------------------------------------------------------- // ------------------------------------------------------------------------- @Override public InputStream getInputStream() throws IOException { BookmarkBuilder builder; ByteArrayOutputStream stream; builder = BookmarkManager.getBookmarkWriter(stream = new ByteArrayOutputStream()); try { builder.startBookmarks(); builder.addBookmark(bookmark.getName(), bookmark.getLocation()); builder.endBookmarks(); } // If an exception occured, we have to look for its root cause. catch(Throwable e) { Throwable e2; // Looks for the cause. while((e2 = e.getCause()) != null) e = e2; // If the cause is an IOException, thow it. if(e instanceof IOException) throw (IOException)e; // Otherwise, throw the exception as an IOException with a the underlying cause's message. throw new IOException(e.getMessage()); } return new ByteArrayInputStream(stream.toByteArray()); } @Override public OutputStream getOutputStream() throws IOException {return new BookmarkOutputStream();} // - Unused methods -------------------------------------------------------- // ------------------------------------------------------------------------- // The following methods are not used by BookmarkFile. They will throw an exception or // return an 'operation non supported' / default value. @Override @UnsupportedFileOperation public void mkdir() throws UnsupportedFileOperationException {throw new UnsupportedFileOperationException(FileOperation.CREATE_DIRECTORY);} @Override public long getDate() {return 0;} @Override public PermissionBits getChangeablePermissions() {return PermissionBits.EMPTY_PERMISSION_BITS;} @Override @UnsupportedFileOperation public void changeDate(long lastModified) throws UnsupportedFileOperationException {throw new UnsupportedFileOperationException(FileOperation.CHANGE_DATE);} @Override public long getSize() {return -1;} @Override @UnsupportedFileOperation public RandomAccessInputStream getRandomAccessInputStream() throws UnsupportedFileOperationException {throw new UnsupportedFileOperationException(FileOperation.RANDOM_READ_FILE);} @Override @UnsupportedFileOperation public OutputStream getAppendOutputStream() throws UnsupportedFileOperationException {throw new UnsupportedFileOperationException(FileOperation.APPEND_FILE);} @Override @UnsupportedFileOperation public RandomAccessOutputStream getRandomAccessOutputStream() throws UnsupportedFileOperationException {throw new UnsupportedFileOperationException(FileOperation.RANDOM_WRITE_FILE);} @Override public Object getUnderlyingFileObject() {return null;} @Override public boolean isSymlink() {return false;} @Override public boolean isSystem() {return false;} @Override public String getOwner() {return null;} @Override public boolean canGetOwner() {return false;} @Override public String getGroup() {return null;} @Override public boolean canGetGroup() {return false;} }