package org.jnode.fs.hfsplus.compression; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.util.LinkedHashMap; import java.util.Map; import org.jnode.fs.hfsplus.HfsPlusFile; import org.jnode.fs.hfsplus.HfsPlusFileSystem; import org.jnode.fs.hfsplus.attributes.AttributeData; /** * An implementation of {@link org.jnode.fs.hfsplus.attributes.AttributeData} which reads out compressed data from * another attribute. * * @author Luke Quinane */ public class CompressedAttributeData extends AttributeData implements Closeable { /** * The HFS+ file. */ private HfsPlusFile file; /** * The attribute data which has the compressed copy of the data. */ private AttributeData attributeData; /** * The compression on-disk header. */ private DecmpfsDiskHeader decmpfsDiskHeader; /** * The decompressor to use to read back the data. */ private HfsPlusCompression decompressor; public CompressedAttributeData(HfsPlusFileSystem fs, HfsPlusFile file, AttributeData attributeData) { this.file = file; this.attributeData = attributeData; try { ByteBuffer buffer = ByteBuffer.allocate(DecmpfsDiskHeader.LENGTH); attributeData.read(fs, 0, buffer); decmpfsDiskHeader = new DecmpfsDiskHeader(buffer.array(), 0); } catch (IOException e) { throw new IllegalStateException("Error reading compression disk header", e); } } @Override public long getSize() { return decmpfsDiskHeader.getUncompressedSize(); } /** * Checks whether the compressed data is stored in the file's resource fork. * * @return {@code true} if compressed in the resource fork. */ public boolean isCompressedInFork() { return decmpfsDiskHeader.getType() == DecmpfsDiskHeader.COMPRESSION_TYPE_ZLIB_FORK || decmpfsDiskHeader.getType() == DecmpfsDiskHeader.COMPRESSION_TYPE_LZVN_FORK; } /** * Gets the compression type. * * @return the compression type. */ public long getCompressionType() { return decmpfsDiskHeader.getType(); } @Override public void read(HfsPlusFileSystem fs, long fileOffset, ByteBuffer dest) throws IOException { if (decompressor == null) { if (!fs.getRegisteredCompressionTypes().containsKey(decmpfsDiskHeader.getType())) { throw new UnsupportedOperationException("Unsupported compression type: " + decmpfsDiskHeader); } HfsPlusCompressionFactory factory = fs.getRegisteredCompressionTypes().get(decmpfsDiskHeader.getType()); decompressor = factory.createDecompressor(file, attributeData, decmpfsDiskHeader); } decompressor.read(fs, fileOffset, dest); } @Override public void close() throws IOException { if (decompressor instanceof Closeable) { ((Closeable) decompressor).close(); } } /** * Gets the map of the default supported compression types. * * @return the map. */ public static Map<Long, HfsPlusCompressionFactory> getDefaultTypes() { Map<Long, HfsPlusCompressionFactory> compressionTypeMap = new LinkedHashMap<Long, HfsPlusCompressionFactory>(); compressionTypeMap.put(DecmpfsDiskHeader.COMPRESSION_TYPE1, new AttributeType1Compression.Factory()); compressionTypeMap.put(DecmpfsDiskHeader.COMPRESSION_TYPE_ZLIB, new AttributeZlibCompression.Factory()); compressionTypeMap.put(DecmpfsDiskHeader.COMPRESSION_TYPE_ZLIB_FORK, new ZlibForkCompression.Factory()); compressionTypeMap.put(DecmpfsDiskHeader.COMPRESSION_TYPE_LZVN, new AttributeLzvnCompression.Factory()); compressionTypeMap.put(DecmpfsDiskHeader.COMPRESSION_TYPE_LZVN_FORK, new LzvnForkCompression.Factory()); return compressionTypeMap; } }