diff options
Diffstat (limited to 'src/main/java/de/waldheinz/fs/fat/FatFile.java')
-rw-r--r-- | src/main/java/de/waldheinz/fs/fat/FatFile.java | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/main/java/de/waldheinz/fs/fat/FatFile.java b/src/main/java/de/waldheinz/fs/fat/FatFile.java new file mode 100644 index 0000000..95942e0 --- /dev/null +++ b/src/main/java/de/waldheinz/fs/fat/FatFile.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2009,2010 Matthias Treydte <mt@waldheinz.de> + * + * This library 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 2.1 of the License, or + * (at your option) any later version. + * + * This library 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 library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package de.waldheinz.fs.fat; + +import de.waldheinz.fs.AbstractFsObject; +import java.io.IOException; +import de.waldheinz.fs.FsFile; +import de.waldheinz.fs.ReadOnlyException; +import java.io.EOFException; +import java.nio.ByteBuffer; + +/** + * The in-memory representation of a single file (chain of clusters) on a + * FAT file system. + * + * @author Matthias Treydte <waldheinz at gmail.com> + * @since 0.6 + */ +public final class FatFile extends AbstractFsObject implements FsFile { + private final FatDirectoryEntry entry; + private final ClusterChain chain; + + private FatFile(FatDirectoryEntry myEntry, ClusterChain chain) { + super(myEntry.isReadOnly()); + + this.entry = myEntry; + this.chain = chain; + } + + static FatFile get(Fat fat, FatDirectoryEntry entry) + throws IOException { + + if (entry.isDirectory()) + throw new IllegalArgumentException(entry + " is a directory"); + + final ClusterChain cc = new ClusterChain( + fat, entry.getStartCluster(), entry.isReadonlyFlag()); + + if (entry.getLength() > cc.getLengthOnDisk()) throw new IOException( + "entry is larger than associated cluster chain"); + + return new FatFile(entry, cc); + } + + /** + * Returns the length of this file in bytes. This is the length that + * is stored in the directory entry that is associated with this file. + * + * @return long the length that is recorded for this file + */ + @Override + public long getLength() { + checkValid(); + + return entry.getLength(); + } + + /** + * Sets the size (in bytes) of this file. Because + * {@link #write(long, java.nio.ByteBuffer) writing} to the file will grow + * it automatically if needed, this method is mainly usefull for truncating + * a file. + * + * @param length the new length of the file in bytes + * @throws ReadOnlyException if this file is read-only + * @throws IOException on error updating the file size + */ + @Override + public void setLength(long length) throws ReadOnlyException, IOException { + checkWritable(); + + if (getLength() == length) return; + + updateTimeStamps(true); + chain.setSize(length); + + this.entry.setStartCluster(chain.getStartCluster()); + this.entry.setLength(length); + } + + /** + * <p> + * {@inheritDoc} + * </p><p> + * Unless this file is {@link #isReadOnly() read-ony}, this method also + * updates the "last accessed" field in the directory entry that is + * associated with this file. + * </p> + * + * @param offset {@inheritDoc} + * @param dest {@inheritDoc} + * @see FatDirectoryEntry#setLastAccessed(long) + */ + @Override + public void read(long offset, ByteBuffer dest) throws IOException { + checkValid(); + + final int len = dest.remaining(); + + if (len == 0) return; + + if (offset + len > getLength()) { + throw new EOFException(); + } + + if (!isReadOnly()) { + updateTimeStamps(false); + } + + chain.readData(offset, dest); + } + + /** + * <p> + * {@inheritDoc} + * </p><p> + * If the data to be written extends beyond the current + * {@link #getLength() length} of this file, an attempt is made to + * {@link #setLength(long) grow} the file so that the data will fit. + * Additionally, this method updates the "last accessed" and "last modified" + * fields on the directory entry that is associated with this file. + * </p> + * + * @param offset {@inheritDoc} + * @param srcBuf {@inheritDoc} + */ + @Override + public void write(long offset, ByteBuffer srcBuf) + throws ReadOnlyException, IOException { + + checkWritable(); + + updateTimeStamps(true); + + final long lastByte = offset + srcBuf.remaining(); + + if (lastByte > getLength()) { + setLength(lastByte); + } + + chain.writeData(offset, srcBuf); + } + + private void updateTimeStamps(boolean write) { + final long now = System.currentTimeMillis(); + entry.setLastAccessed(now); + + if (write) { + entry.setLastModified(now); + } + } + + /** + * Has no effect besides possibly throwing an {@code ReadOnlyException}. To + * make sure that all data is written out to disk use the + * {@link FatFileSystem#flush()} method. + * + * @throws ReadOnlyException if this {@code FatFile} is read-only + */ + @Override + public void flush() throws ReadOnlyException { + checkWritable(); + + /* nothing else to do */ + } + + /** + * Returns the {@code ClusterChain} that holds the contents of + * this {@code FatFile}. + * + * @return the file's {@code ClusterChain} + */ + ClusterChain getChain() { + checkValid(); + + return chain; + } + + /** + * Returns a human-readable string representation of this {@code FatFile}, + * mainly for debugging purposes. + * + * @return a string describing this {@code FatFile} + */ + @Override + public String toString() { + return getClass().getSimpleName() + " [length=" + getLength() + + ", first cluster=" + chain.getStartCluster() + "]"; + } + +} |