001    /*
002     * This file is part of muCommander, http://www.mucommander.com
003     * Copyright (C) 2002-2008 Maxence Bernard
004     *
005     * muCommander is free software; you can redistribute it and/or modify
006     * it under the terms of the GNU General Public License as published by
007     * the Free Software Foundation; either version 3 of the License, or
008     * (at your option) any later version.
009     *
010     * muCommander is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    
019    package com.mucommander.file.impl.bzip2;
020    
021    import com.mucommander.file.AbstractFile;
022    import com.mucommander.file.AbstractROArchiveFile;
023    import com.mucommander.file.ArchiveEntry;
024    import org.apache.tools.bzip2.CBZip2InputStream;
025    
026    import java.io.BufferedInputStream;
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.util.Vector;
030    
031    /**
032     * Bzip2ArchiveFile provides read-only access to archives in the Bzip2 format.
033     *
034     * <p>The actual decompression work is performed by the <code>Apache Ant</code> library under the terms of the
035     * Apache Software License.</p>
036     *
037     * @see com.mucommander.file.impl.bzip2.Bzip2FormatProvider
038     * @author Maxence Bernard
039     */
040    public class Bzip2ArchiveFile extends AbstractROArchiveFile {
041    
042        /**
043         * Creates a BzipArchiveFile on top of the given file.
044         */
045        public Bzip2ArchiveFile(AbstractFile file) {
046            super(file);
047        }
048    
049    
050        ////////////////////////////////////////
051        // AbstractArchiveFile implementation //
052        ////////////////////////////////////////
053            
054        public Vector getEntries() throws IOException {
055            String extension = getExtension();
056            String name = getName();
057                    
058            if(extension!=null) {
059                extension = extension.toLowerCase();
060                            
061                // Remove the 'bz2' or 'tbz2' extension from the entry's name
062                if(extension.equals("tbz2"))
063                    name = name.substring(0, name.length()-4)+"tar";
064                else if(extension.equals("bz2"))
065                    name = name.substring(0, name.length()-4);
066            }
067    
068            Vector entries = new Vector();
069            entries.add(new ArchiveEntry("/"+name, false, getDate(), -1));
070            return entries;
071        }
072    
073    
074        public InputStream getEntryInputStream(ArchiveEntry entry) throws IOException {
075            try {
076                InputStream in = getInputStream();
077    
078                // Skips the 2 magic bytes 'BZ', as required by CBZip2InputStream. Quoted from CBZip2InputStream's Javadoc:
079                // "Although BZip2 headers are marked with the magic 'Bz'. this constructor expects the next byte in the
080                // stream to be the first one after the magic.  Thus callers have to skip the first two bytes. Otherwise
081                // this constructor will throw an exception."
082                // Note: the return value of read() is unchecked. In the unlikely event that EOF is reached in the first
083                // 2 bytes, CBZip2InputStream will throw an IOException.
084                in.read();
085                in.read();
086    
087                // Quoted from CBZip2InputStream's Javadoc:
088                // "CBZip2InputStream reads bytes from the compressed source stream via the single byte {@link java.io.InputStream#read()
089                // read()} method exclusively. Thus you should consider to use a buffered source stream."
090                return new CBZip2InputStream(new BufferedInputStream(in));
091            }
092            catch(Exception e) {
093                // CBZip2InputStream is known to throw NullPointerException if file is not properly Bzip2-encoded
094                // so we need to catch those and throw them as IOException
095                if(com.mucommander.Debug.ON) com.mucommander.Debug.trace("Exception caught while creating CBZip2InputStream:"+e+", throwing IOException"); 
096    
097                throw new IOException();
098            }
099        }
100    }