/** * 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 Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.commons.file; import com.mucommander.commons.file.*; import com.mucommander.commons.file.filter.FileFilter; import com.mucommander.commons.file.filter.FilenameFilter; import com.mucommander.commons.io.FileTransferException; import com.mucommander.commons.io.RandomAccessInputStream; import com.mucommander.commons.io.RandomAccessOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; /** * ProxyFile is an {@link AbstractFile} that acts as a proxy between the class that extends it * and the proxied <code>AbstractFile</code> instance specified to the constructor. * All <code>AbstractFile</code> public methods (abstract or not) are delegated to the proxied file. * The {@link #getProxiedFile()} method allows to retrieve the proxied file instance. * * <p>This class is useful for wrapper files, such as {@link com.mucommander.commons.file.archive.AbstractArchiveFile archive files}, * that provide additional functionalities over an existing <code>AbstractFile</code> instance (the proxied file). * By implementing/overriding every <code>AbstractFile</code> methods, <code>ProxyFile</code> ensures that * all <code>AbstractFile</code> methods can safely be used, even if they are overridden by the proxied * file instance's class. * * <p><b>Implementation note:</b> the <code>java.lang.reflect.Proxy</code> class can unfortunately not be * used as it only works with interfaces (not abstract class). There doesn't seem to be any dynamic way to * proxy method invocations, so any modifications made to {@link com.mucommander.commons.file.AbstractFile} must be also * reflected in <code>ProxyFile</code>. * * @see com.mucommander.commons.file.archive.AbstractArchiveFile * @author Maxence Bernard */ public abstract class ProxyFile extends AbstractFile { private static final Logger LOGGER = LoggerFactory.getLogger(ProxyFile.class); /** The proxied file instance */ protected AbstractFile file; /** * Creates a new ProxyFile using the given file to delegate AbstractFile method calls to. * * @param file the file to be proxied */ public ProxyFile(AbstractFile file) { super(file.getURL()); this.file = file; } /** * Returns the <code>AbstractFile</code> instance proxied by this </code>ProxyFile</code>. * * @return the <code>AbstractFile</code> instance proxied by this </code>ProxyFile</code> */ public AbstractFile getProxiedFile() { return file; } ///////////////////////////////// // AbstractFile implementation // ///////////////////////////////// @Override public long getDate() { return file.getDate(); } @Override public void changeDate(long lastModified) throws IOException, UnsupportedFileOperationException { file.changeDate(lastModified); } @Override public long getSize() { return file.getSize(); } @Override public AbstractFile getParent() { return file.getParent(); } @Override public void setParent(AbstractFile parent) { file.setParent(parent); } @Override public boolean exists() { return file.exists(); } @Override public void changePermission(PermissionAccess access, PermissionType permission, boolean enabled) throws IOException, UnsupportedFileOperationException { file.changePermission(access, permission, enabled); } @Override public String getOwner() { return file.getOwner(); } @Override public boolean canGetOwner() { return file.canGetOwner(); } @Override public String getGroup() { return file.getGroup(); } @Override public boolean canGetGroup() { return file.canGetGroup(); } @Override public boolean isDirectory() { return file.isDirectory(); } @Override public boolean isSymlink() { return file.isSymlink(); } @Override public boolean isSystem() { return file.isSystem(); } @Override public AbstractFile[] ls() throws IOException, UnsupportedFileOperationException { return file.ls(); } @Override public void mkdir() throws IOException, UnsupportedFileOperationException { file.mkdir(); } @Override public InputStream getInputStream() throws IOException, UnsupportedFileOperationException { return file.getInputStream(); } @Override public OutputStream getOutputStream() throws IOException, UnsupportedFileOperationException { return file.getOutputStream(); } @Override public OutputStream getAppendOutputStream() throws IOException, UnsupportedFileOperationException { return file.getAppendOutputStream(); } @Override public RandomAccessInputStream getRandomAccessInputStream() throws IOException, UnsupportedFileOperationException { return file.getRandomAccessInputStream(); } @Override public RandomAccessOutputStream getRandomAccessOutputStream() throws IOException, UnsupportedFileOperationException { return file.getRandomAccessOutputStream(); } @Override public void delete() throws IOException, UnsupportedFileOperationException { file.delete(); } @Override public void copyRemotelyTo(AbstractFile destFile) throws IOException, UnsupportedFileOperationException { file.copyRemotelyTo(destFile); } @Override public void renameTo(AbstractFile destFile) throws IOException, UnsupportedFileOperationException { file.renameTo(destFile); } @Override public long getFreeSpace() throws IOException, UnsupportedFileOperationException { return file.getFreeSpace(); } @Override public long getTotalSpace() throws IOException, UnsupportedFileOperationException { return file.getTotalSpace(); } @Override public Object getUnderlyingFileObject() { return file.getUnderlyingFileObject(); } ///////////////////////////////////// // Overridden AbstractFile methods // ///////////////////////////////////// @Override public final boolean isFileOperationSupported(FileOperation op) { Class<? extends AbstractFile> thisClass = getClass(); Method opMethod = op.getCorrespondingMethod(thisClass); // If the method corresponding to the file operation has been overridden by this class (a ProxyFile subclass), // check the presence of the UnsupportedFileOperation annotation in this class. try { if(!thisClass.getMethod(opMethod.getName(), opMethod.getParameterTypes()).getDeclaringClass().equals(ProxyFile.class)) return AbstractFile.isFileOperationSupported(op, thisClass); } catch(Exception e) { // Should never happen, unless AbstractFile method signatures have changed and FileOperation has not been updated LOGGER.warn("Exception caught, this should not have happened", e); } // Otherwise, check for the presence of the UnsupportedFileOperation annotation in the wrapped AbstractFile. return file.isFileOperationSupported(op); } @Override public FileURL getURL() { return file.getURL(); } @Override public URL getJavaNetURL() throws MalformedURLException { return file.getJavaNetURL(); } @Override public String getName() { return file.getName(); } @Override public String getExtension() { return file.getExtension(); } @Override public String getBaseName() { return file.getBaseName(); } @Override public String getAbsolutePath() { return file.getAbsolutePath(); } @Override public String getCanonicalPath() { return file.getCanonicalPath(); } @Override public AbstractFile getCanonicalFile() { return file.getCanonicalFile(); } @Override public String getSeparator() { return file.getSeparator(); } @Override public boolean isArchive() { return file.isArchive(); } @Override public boolean isHidden() { return file.isHidden(); } @Override public FilePermissions getPermissions() { return file.getPermissions(); } @Override public void changePermissions(int permissions) throws IOException, UnsupportedFileOperationException { file.changePermissions(permissions); } @Override public PermissionBits getChangeablePermissions() { return file.getChangeablePermissions(); } @Override public String getPermissionsString() { return file.getPermissionsString(); } @Override public AbstractFile getRoot() { return file.getRoot(); } @Override public boolean isRoot() { return file.isRoot(); } @Override public AbstractFile getVolume() { return file.getVolume(); } @Override public InputStream getInputStream(long offset) throws IOException, UnsupportedFileOperationException { return file.getInputStream(offset); } @Override public void copyStream(InputStream in, boolean append, long length) throws FileTransferException { file.copyStream(in, append, length); } @Override public AbstractFile[] ls(FileFilter filter) throws IOException, UnsupportedFileOperationException { return file.ls(filter); } @Override public AbstractFile[] ls(FilenameFilter filter) throws IOException, UnsupportedFileOperationException { return file.ls(filter); } @Override public void mkfile() throws IOException, UnsupportedFileOperationException { file.mkfile(); } @Override public void deleteRecursively() throws IOException, UnsupportedFileOperationException { file.deleteRecursively(); } public boolean equals(Object f) { return file.equals(f); } @Override public boolean equalsCanonical(Object f) { return file.equalsCanonical(f); } public int hashCode() { return file.hashCode(); } public String toString() { return file.toString(); } }