/* * JFileSync * Copyright (C) 2002-2007, Jens Heidrich * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA */ package jfs.sync; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import jfs.conf.JFSConfig; import jfs.conf.JFSLog; import jfs.conf.JFSText; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents a directory or a simple file. This object encapsulates the Java File object. * * @author Jens Heidrich * @version $Id: JFSFile.java,v 1.33 2007/07/20 12:27:52 heidrich Exp $ */ public abstract class JFSFile implements Comparable<JFSFile> { private static final Logger LOG = LoggerFactory.getLogger(JFSFile.class); /** * The assigned file producer. */ protected JFSFileProducer fileProducer; /** * The relative path of the JFS file starting from the root JFS file. */ protected String relativePath; /** * Creates a new JFS file from a relative path. * * @param fileProducer * The assigned file producer. * @param relativePath * The relative path of the JFS file starting from the root JFS file. */ protected JFSFile(JFSFileProducer fileProducer, String relativePath) { this.fileProducer = fileProducer; this.relativePath = JFSFormatter.replaceSeparatorChar(relativePath); } /** * Returns the corresponding file object if possible, null otherwise. * * @return File object. */ // public abstract File getFile(); /** * Returns the name of the file. * * @return Name of the file. */ public abstract String getName(); /** * Returns the path of the file starting from the root JFS file. * * @return Path of the file. */ public abstract String getPath(); /** * Returns the assigned file producer. * * @return The file producer. */ public final JFSFileProducer getFileProducer() { return fileProducer; } /** * Returns the relative path of the file. * * @return Path of the file. */ public final String getRelativePath() { return relativePath; } /** * Returns whether the file is a directory. * * @return True if and only if the file is a directory. */ public abstract boolean isDirectory(); /** * Returns whether we can read the file. * * @return True if and only if we can read the file. */ public abstract boolean canRead(); /** * Returns whether we can write to the file. * * @return True if and only if we can write to the file. */ public abstract boolean canWrite(); /** * Returns the length of the file. * * @return Length of the file. */ public abstract long getLength(); /** * Returns the time of the last modification of the file. * * @return Time of last modification of the file. */ public abstract long getLastModified(); /** * Returns the included files. The returned list must be not equal to null. If no children exist, an array of size * zero is returned. * * @return An array of JFSFile objects included in the directory. */ public abstract JFSFile[] getList(); /** * Returns the included directories. * * @return An array of JFSFile objects included in the directory. */ public final JFSFile[] getDirectoryList() { JFSFile[] list = getList(); assert list!=null; int length = 0; for (JFSFile f : list) { if (f.isDirectory()) { length++; } } JFSFile[] directoryList = new JFSFile[length]; int j = 0; for (JFSFile f : list) { if (f.isDirectory()) { directoryList[j] = f; j++; } } return directoryList; } /** * Returns the included files (not directories). * * @return An array of JFSFile objects included in the directory. */ public final JFSFile[] getFileList() { JFSFile[] list = getList(); assert list!=null; int length = 0; for (JFSFile f : list) { if (!f.isDirectory()) { length++; } // if } // for JFSFile[] fileList = new JFSFile[length]; int j = 0; for (JFSFile f : list) { if (!f.isDirectory()) { fileList[j] = f; j++; } } // for return fileList; } /** * Tests whether the file denoted by this abstract pathname exists. * * @return True if and only if the file denoted by this abstract pathname exists; false otherwise. */ public abstract boolean exists(); /** * Creates the directory named by this abstract pathname. * * @return True if and only if the directory was created; false otherwise. */ public abstract boolean mkdir(); /** * Sets the last-modified time of the file or directory named by this abstract pathname. * * @param time The new last-modified time, measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970). * @return True if and only if the operation succeeded; false otherwise. */ public abstract boolean setLastModified(long time); /** * Marks the file or directory named by this abstract pathname so that only read operations are allowed. * * @return True if and only if the operation succeeded; false otherwise. */ public abstract boolean setReadOnly(); /** * Deletes the file or directory denoted by this abstract pathname. If this pathname denotes a directory, then the * directory must be empty in order to be deleted. * * @return True if and only if the file or directory is successfully deleted; false otherwise. */ public abstract boolean delete(); /** * Returns the input stream if the file is not a directory and null if it is a directory or nor stream could be * created. * * @return The input stream. */ protected abstract InputStream getInputStream(); /** * Returns the output stream if the file is not a directory and null if it is a directory or nor stream could be * created. * * @return The output stream. */ protected abstract OutputStream getOutputStream(); /** * Performs operations for closing the created input stream for this JFS file. */ protected abstract void closeInputStream(); /** * Performs operations for closing the created output stream for this JFS file. */ protected abstract void closeOutputStream(); /** * Performs operation before the copy statement on target side, for instance, preparing setting of file attributes * like last modified and can write property. * * @param srcFile The file to copy from. * @return True if and only if the operation was successful. */ protected abstract boolean preCopyTgt(JFSFile srcFile); /** * Performs operation before the copy statement on source side. * * @param tgtFile The file to copy to. * @return True if and only if the operation was successful. */ protected abstract boolean preCopySrc(JFSFile tgtFile); /** * Performs operation after the copy statement on target side, for instance, preparing setting of file attributes * like last modified and can write property. * * @param srcFile The file to copy from. * @return True if and only if the operation was successful. */ protected abstract boolean postCopyTgt(JFSFile srcFile); /** * Performs operation after the copy statement on source side. * * @param tgtFile The file to copy to. * @return True if and only if the operation was successful. */ protected abstract boolean postCopySrc(JFSFile tgtFile); /** * Flushes all changes to the file object if not performed yet. This is done recursively. So, a flush on the root * file performs a flush on all childs. * * @return True if and only if the operation was successful. */ public abstract boolean flush(); /** * Writes the content of the JFS file to a new target file. If this file is a directory false is returned. This * method just copies the contents from this file to the target file, no attributes, like the last modified date or * the can attribute are adopted. * * @param input The input stream of the source file. * @param output The output stream of the target file. * @return True if and only if the file is not a directory and was successfully copied; false otherwise. */ private boolean copy(InputStream input, OutputStream output) { if (isDirectory()) { LOG.warn("copy() directory involved"); return false; } JFSText t = JFSText.getInstance(); JFSProgress progress = JFSProgress.getInstance(); JFSCopyMonitor monitor = JFSCopyMonitor.getInstance(); try { if ((input==null)||(output==null)) { LOG.warn("copy() one of the two files was null"); return false; } byte[] buf = new byte[JFSConfig.getInstance().getBufferSize()]; long length = getLength(); long transferedBytes = 0; int len; int maxLen = JFSConfig.getInstance().getBufferSize(); if (length<maxLen) { maxLen = (int) length; } while (transferedBytes<length&&(len = input.read(buf, 0, maxLen))>0&&!progress.isCanceled()) { output.write(buf, 0, len); transferedBytes += len; long r = length-transferedBytes; if (r<maxLen) { maxLen = (int) r; } monitor.setBytesTransferedCurrentFile(transferedBytes); progress.fireUpdate(); } if (transferedBytes==length) { LOG.info("copy() done"); return true; } LOG.error("copy() could not copy all necessary bytes: "+transferedBytes+" of "+length); return false; } catch (IOException e) { e.printStackTrace(System.out); PrintStream p = JFSLog.getErr().getStream(); p.println(t.get("error.io")); p.println(" '"+this.getPath()+"'"); LOG.error("copy()", e); return false; } } /** * Writes the content of the JFSFile to a new target file. If this JFSFile is a directory the target directory is * made. * * @param tgtFile The Target File. * @return True if and only if the file is successfully copied; false otherwise. */ public final boolean copy(JFSFile tgtFile) { // Test whether the source file (this) can be read by the application // and the target file (tgtFile) can be written to. If not, false is // returned: if (!canRead()||!tgtFile.canWrite()) { LOG.error("copy() cannot read src or write target: "+canRead()+" / "+tgtFile.canWrite()); return false; } boolean success = tgtFile.preCopyTgt(this); success = success&&preCopySrc(tgtFile); if (isDirectory()) { success = success&&tgtFile.mkdir(); } else { success = success&©(getInputStream(), tgtFile.getOutputStream()); closeInputStream(); tgtFile.closeOutputStream(); } success = success&&tgtFile.postCopyTgt(this); success = success&&postCopySrc(tgtFile); if (!success||(JFSProgress.getInstance().isCanceled())) { tgtFile.delete(); success = false; } return success; } /** * Returns the result of the comparison of the names of two JFSFile objects. * * @param jfsFile The file object to compare the current object with. * @return Result of the comparison. */ @Override public final int compareTo(JFSFile jfsFile) { return this.getName().compareTo(jfsFile.getName()); } @Override public String toString() { return getName(); } // toString() }