You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tc...@apache.org on 2008/07/10 12:17:49 UTC

svn commit: r675498 [4/6] - in /commons/sandbox/compress/branches/redesign: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/commons/ src/main/java/org/apache/commons/compress/ src/main/java/org/apa...

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,731 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.ZipException;
+
+/**
+ * Reimplementation of {@link java.util.zip.ZipOutputStream
+ * java.util.zip.ZipOutputStream} that does handle the extended functionality of
+ * this package, especially internal/external file attributes and extra fields
+ * with different layouts for local file data and central directory entries. <p>
+ *
+ * This implementation will use a Data Descriptor to store size and CRC
+ * information for DEFLATED entries, this means, you don't need to calculate
+ * them yourself. Unfortunately this is not possible for the STORED method, here
+ * setting the CRC and uncompressed size information is required before {@link
+ * #putNextEntry putNextEntry} will be called.</p>
+ *
+ * @author <a href="stefan.bodewig@epost.de">Stefan Bodewig</a>
+ * @version $Revision$
+ */
+public class ZipOutputStream
+    extends DeflaterOutputStream
+{
+    /**
+     * Helper, a 0 as ZipShort.
+     *
+     * @since 1.1
+     */
+    private static final byte[] ZERO = {0, 0};
+
+    /**
+     * Helper, a 0 as ZipLong.
+     *
+     * @since 1.1
+     */
+    private static final byte[] LZERO = {0, 0, 0, 0};
+
+    /**
+     * Compression method for deflated entries.
+     *
+     * @since 1.1
+     */
+    public static final int DEFLATED = ZipEntry.DEFLATED;
+
+    /**
+     * Compression method for deflated entries.
+     *
+     * @since 1.1
+     */
+    public static final int STORED = ZipEntry.STORED;
+
+    /*
+     * Various ZIP constants
+     */
+    /**
+     * local file header signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong LFH_SIG = new ZipLong( 0X04034B50L );
+    /**
+     * data descriptor signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong DD_SIG = new ZipLong( 0X08074B50L );
+    /**
+     * central file header signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong CFH_SIG = new ZipLong( 0X02014B50L );
+    /**
+     * end of central dir signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong EOCD_SIG = new ZipLong( 0X06054B50L );
+
+    /**
+     * Smallest date/time ZIP can handle.
+     *
+     * @since 1.1
+     */
+    private static final ZipLong DOS_TIME_MIN = new ZipLong( 0x00002100L );
+
+    /**
+     * The file comment.
+     *
+     * @since 1.1
+     */
+    private String m_comment = "";
+
+    /**
+     * Compression level for next entry.
+     *
+     * @since 1.1
+     */
+    private int m_level = Deflater.DEFAULT_COMPRESSION;
+
+    /**
+     * Default compression method for next entry.
+     *
+     * @since 1.1
+     */
+    private int m_method = DEFLATED;
+
+    /**
+     * List of ZipEntries written so far.
+     *
+     * @since 1.1
+     */
+    private final ArrayList m_entries = new ArrayList();
+
+    /**
+     * CRC instance to avoid parsing DEFLATED data twice.
+     *
+     * @since 1.1
+     */
+    private final CRC32 m_crc = new CRC32();
+
+    /**
+     * Count the bytes written to out.
+     *
+     * @since 1.1
+     */
+    private long m_written;
+
+    /**
+     * Data for current entry started here.
+     *
+     * @since 1.1
+     */
+    private long m_dataStart;
+
+    /**
+     * Start of central directory.
+     *
+     * @since 1.1
+     */
+    private ZipLong m_cdOffset = new ZipLong( 0 );
+
+    /**
+     * Length of central directory.
+     *
+     * @since 1.1
+     */
+    private ZipLong m_cdLength = new ZipLong( 0 );
+
+    /**
+     * Holds the offsets of the LFH starts for each entry
+     *
+     * @since 1.1
+     */
+    private final Hashtable m_offsets = new Hashtable();
+
+    /**
+     * The encoding to use for filenames and the file comment. <p>
+     *
+     * For a list of possible values see <a
+     * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">
+     * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
+     * </a>. Defaults to the platform's default character encoding.</p>
+     *
+     * @since 1.3
+     */
+    private String m_encoding;
+
+    /**
+     * Current entry.
+     *
+     * @since 1.1
+     */
+    private ZipArchiveEntry m_entry;
+
+    /**
+     * Creates a new ZIP OutputStream filtering the underlying stream.
+     *
+     * @param output the output stream to write to
+     * @since 1.1
+     */
+    public ZipOutputStream( final OutputStream output )
+    {
+        super( output, new Deflater( Deflater.DEFAULT_COMPRESSION, true ) );
+    }
+
+    /**
+     * Convert a Date object to a DOS date/time field. <p>
+     *
+     * Stolen from InfoZip's <code>fileio.c</code></p>
+     *
+     * @param time Description of Parameter
+     * @return Description of the Returned Value
+     * @since 1.1
+     */
+    protected static ZipLong toDosTime( Date time )
+    {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime( time );
+        int year = cal.get(Calendar.YEAR);
+        int month = cal.get(Calendar.MONTH) + 1;
+        if( year < 1980 )
+        {
+            return DOS_TIME_MIN;
+        }
+        long value = ( ( year - 1980 ) << 25 )
+            | ( month << 21 )
+            | ( cal.get(Calendar.DAY_OF_MONTH) << 16 )
+            | ( cal.get(Calendar.HOUR_OF_DAY) << 11 )
+            | ( cal.get(Calendar.MINUTE) << 5 )
+            | ( cal.get(Calendar.SECOND) >> 1 );
+
+        byte[] result = new byte[ 4 ];
+        result[ 0 ] = (byte)( ( value & 0xFF ) );
+        result[ 1 ] = (byte)( ( value & 0xFF00 ) >> 8 );
+        result[ 2 ] = (byte)( ( value & 0xFF0000 ) >> 16 );
+        result[ 3 ] = (byte)( ( value & 0xFF000000l ) >> 24 );
+        return new ZipLong( result );
+    }
+
+    /**
+     * Set the file comment.
+     *
+     * @param comment The new Comment value
+     * @since 1.1
+     */
+    public void setComment( String comment )
+    {
+        m_comment = comment;
+    }
+
+    /**
+     * The encoding to use for filenames and the file comment. <p>
+     *
+     * For a list of possible values see <a
+     * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">
+     * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
+     * </a>. Defaults to the platform's default character encoding.</p>
+     *
+     * @param encoding The new Encoding value
+     * @since 1.3
+     */
+    public void setEncoding( String encoding )
+    {
+        m_encoding = encoding;
+    }
+
+    /**
+     * Sets the compression level for subsequent entries. <p>
+     *
+     * Default is Deflater.DEFAULT_COMPRESSION.</p>
+     *
+     * @param level The new Level value
+     * @since 1.1
+     */
+    public void setLevel( int level )
+    {
+        m_level = level;
+    }
+
+    /**
+     * Sets the default compression method for subsequent entries. <p>
+     *
+     * Default is DEFLATED.</p>
+     *
+     * @param method The new Method value
+     * @since 1.1
+     */
+    public void setMethod( final int method )
+    {
+        m_method = method;
+    }
+
+    /**
+     * The encoding to use for filenames and the file comment.
+     *
+     * @return null if using the platform's default character encoding.
+     * @since 1.3
+     */
+    public String getEncoding()
+    {
+        return m_encoding;
+    }
+
+    /**
+     * Writes all necessary data for this entry.
+     *
+     * @throws IOException if an IO failure causes operation to fail
+     * @since 1.1
+     */
+    public void closeEntry()
+        throws IOException
+    {
+        if( m_entry == null )
+        {
+            return;
+        }
+
+        long realCrc = m_crc.getValue();
+        m_crc.reset();
+
+        if( m_entry.getMethod() == DEFLATED )
+        {
+            def.finish();
+            while( !def.finished() )
+            {
+                deflate();
+            }
+
+            m_entry.setSize( def.getTotalIn() );
+            m_entry.setComprSize( def.getTotalOut() );
+            m_entry.setCrc( realCrc );
+
+            def.reset();
+
+            m_written += m_entry.getCompressedSize();
+        }
+        else
+        {
+            if( m_entry.getCrc() != realCrc )
+            {
+                throw new ZipException( "bad CRC checksum for entry "
+                                        + m_entry.getName() + ": "
+                                        + Long.toHexString( m_entry.getCrc() )
+                                        + " instead of "
+                                        + Long.toHexString( realCrc ) );
+            }
+
+            if( m_entry.getSize() != m_written - m_dataStart )
+            {
+                throw new ZipException( "bad size for entry "
+                                        + m_entry.getName() + ": "
+                                        + m_entry.getSize()
+                                        + " instead of "
+                                        + ( m_written - m_dataStart ) );
+            }
+
+        }
+
+        writeDataDescriptor( m_entry );
+        m_entry = null;
+    }
+
+    /*
+     * Found out by experiment, that DeflaterOutputStream.close()
+     * will call finish() - so we don't need to override close
+     * ourselves.
+     */
+    /**
+     * Finishs writing the contents and closes this as well as the underlying
+     * stream.
+     *
+     * @throws IOException if an IO failure causes operation to fail
+     * @since 1.1
+     */
+    public void finish()
+        throws IOException
+    {
+        closeEntry();
+        m_cdOffset = new ZipLong( m_written );
+        final int size = m_entries.size();
+        for( int i = 0; i < size; i++ )
+        {
+            final ZipArchiveEntry entry = (ZipArchiveEntry)m_entries.get( i );
+            writeCentralFileHeader( entry );
+        }
+        m_cdLength = new ZipLong( m_written - m_cdOffset.getValue() );
+        writeCentralDirectoryEnd();
+        m_offsets.clear();
+        m_entries.clear();
+    }
+
+    /**
+     * Begin writing next entry.
+     *
+     * @param entry the entry
+     * @throws IOException if an IO failure causes operation to fail
+     * @since 1.1
+     */
+    public void putNextEntry( final ZipArchiveEntry entry )
+        throws IOException
+    {
+        closeEntry();
+
+        m_entry = entry;
+        m_entries.add( m_entry );
+
+        if( m_entry.getMethod() == -1 )
+        {// not specified
+            m_entry.setMethod( m_method );
+        }
+
+        if( m_entry.getTime() == -1 )
+        {// not specified
+            m_entry.setTime( System.currentTimeMillis() );
+        }
+
+        if( m_entry.getMethod() == STORED )
+        {
+            if( m_entry.getSize() == -1 )
+            {
+                throw new ZipException( "uncompressed size is required for STORED method" );
+            }
+            if( m_entry.getCrc() == -1 )
+            {
+                throw new ZipException( "crc checksum is required for STORED method" );
+            }
+            m_entry.setComprSize( m_entry.getSize() );
+        }
+        else
+        {
+            def.setLevel( m_level );
+        }
+        writeLocalFileHeader( m_entry );
+    }
+
+    /**
+     * Writes bytes to ZIP entry. <p>
+     *
+     * Override is necessary to support STORED entries, as well as calculationg
+     * CRC automatically for DEFLATED entries.</p>
+     *
+     * @param buffer the buffer to write to
+     * @param offset the offset to write to
+     * @param length the length of data to write
+     * @exception IOException if an IO error causes operation to fail
+     */
+    public void write( final byte[] buffer,
+                       final int offset,
+                       final int length )
+        throws IOException
+    {
+        if( m_entry.getMethod() == DEFLATED )
+        {
+            super.write( buffer, offset, length );
+        }
+        else
+        {
+            out.write( buffer, offset, length );
+            m_written += length;
+        }
+        m_crc.update( buffer, offset, length );
+    }
+
+    /**
+     * Retrieve the bytes for the given String in the encoding set for this
+     * Stream.
+     *
+     * @param name the name to decode
+     * @return the bytes for string
+     * @exception ZipException if fail to retrieve bytes for specified string
+     * @since 1.3
+     */
+    protected byte[] getBytes( String name )
+        throws ZipException
+    {
+        if( m_encoding == null )
+        {
+            return name.getBytes();
+        }
+        else
+        {
+            try
+            {
+                return name.getBytes( m_encoding );
+            }
+            catch( UnsupportedEncodingException uee )
+            {
+                throw new ZipException( uee.getMessage() );
+            }
+        }
+    }
+
+    /**
+     * Writes the &quot;End of central dir record&quot;
+     *
+     * @exception IOException when an IO erro causes operation to fail
+     * @since 1.1
+     */
+    protected void writeCentralDirectoryEnd()
+        throws IOException
+    {
+        out.write( EOCD_SIG.getBytes() );
+
+        // disk numbers
+        out.write( ZERO );
+        out.write( ZERO );
+
+        // number of entries
+        byte[] num = ( new ZipShort( m_entries.size() ) ).getBytes();
+        out.write( num );
+        out.write( num );
+
+        // length and location of CD
+        out.write( m_cdLength.getBytes() );
+        out.write( m_cdOffset.getBytes() );
+
+        // ZIP file comment
+        byte[] data = getBytes( m_comment );
+        out.write( ( new ZipShort( data.length ) ).getBytes() );
+        out.write( data );
+    }
+
+    /**
+     * Writes the central file header entry
+     *
+     * @param entry the zip entry
+     * @throws IOException when an IO error causes operation to fail
+     * @since 1.1
+     */
+    protected void writeCentralFileHeader( final ZipArchiveEntry entry )
+        throws IOException
+    {
+        out.write( CFH_SIG.getBytes() );
+        m_written += 4;
+
+        // version made by
+        out.write( ( new ZipShort( 20 ) ).getBytes() );
+        m_written += 2;
+
+        // version needed to extract
+        // general purpose bit flag
+        if( entry.getMethod() == DEFLATED )
+        {
+            // requires version 2 as we are going to store length info
+            // in the data descriptor
+            out.write( ( new ZipShort( 20 ) ).getBytes() );
+
+            // bit3 set to signal, we use a data descriptor
+            out.write( ( new ZipShort( 8 ) ).getBytes() );
+        }
+        else
+        {
+            out.write( ( new ZipShort( 10 ) ).getBytes() );
+            out.write( ZERO );
+        }
+        m_written += 4;
+
+        // compression method
+        out.write( ( new ZipShort( entry.getMethod() ) ).getBytes() );
+        m_written += 2;
+
+        // last mod. time and date
+        out.write( toDosTime( new Date( entry.getTime() ) ).getBytes() );
+        m_written += 4;
+
+        // CRC
+        // compressed length
+        // uncompressed length
+        out.write( ( new ZipLong( entry.getCrc() ) ).getBytes() );
+        out.write( ( new ZipLong( entry.getCompressedSize() ) ).getBytes() );
+        out.write( ( new ZipLong( entry.getSize() ) ).getBytes() );
+        m_written += 12;
+
+        // file name length
+        byte[] name = getBytes( entry.getName() );
+        out.write( ( new ZipShort( name.length ) ).getBytes() );
+        m_written += 2;
+
+        // extra field length
+        byte[] extra = entry.getCentralDirectoryExtra();
+        out.write( ( new ZipShort( extra.length ) ).getBytes() );
+        m_written += 2;
+
+        // file comment length
+        String comm = entry.getComment();
+        if( comm == null )
+        {
+            comm = "";
+        }
+        byte[] comment = getBytes( comm );
+        out.write( ( new ZipShort( comment.length ) ).getBytes() );
+        m_written += 2;
+
+        // disk number start
+        out.write( ZERO );
+        m_written += 2;
+
+        // internal file attributes
+        out.write( ( new ZipShort( entry.getInternalAttributes() ) ).getBytes() );
+        m_written += 2;
+
+        // external file attributes
+        out.write( ( new ZipLong( entry.getExternalAttributes() ) ).getBytes() );
+        m_written += 4;
+
+        // relative offset of LFH
+        out.write( ( (ZipLong)m_offsets.get( entry ) ).getBytes() );
+        m_written += 4;
+
+        // file name
+        out.write( name );
+        m_written += name.length;
+
+        // extra field
+        out.write( extra );
+        m_written += extra.length;
+
+        // file comment
+        out.write( comment );
+        m_written += comment.length;
+    }
+
+    /**
+     * Writes the data descriptor entry
+     *
+     * @param ze Description of Parameter
+     * @throws IOException if an IO failure causes operation to fail
+     * @since 1.1
+     */
+    protected void writeDataDescriptor( ZipArchiveEntry ze )
+        throws IOException
+    {
+        if( ze.getMethod() != DEFLATED )
+        {
+            return;
+        }
+        out.write( DD_SIG.getBytes() );
+        out.write( ( new ZipLong( m_entry.getCrc() ) ).getBytes() );
+        out.write( ( new ZipLong( m_entry.getCompressedSize() ) ).getBytes() );
+        out.write( ( new ZipLong( m_entry.getSize() ) ).getBytes() );
+        m_written += 16;
+    }
+
+    /**
+     * Writes the local file header entry
+     *
+     * @param entry the zip entry
+     * @exception IOException when an IO error causes operation to fail
+     * @since 1.1
+     */
+    protected void writeLocalFileHeader( final ZipArchiveEntry entry )
+        throws IOException
+    {
+        m_offsets.put( entry, new ZipLong( m_written ) );
+
+        out.write( LFH_SIG.getBytes() );
+        m_written += 4;
+
+        // version needed to extract
+        // general purpose bit flag
+        if( entry.getMethod() == DEFLATED )
+        {
+            // requires version 2 as we are going to store length info
+            // in the data descriptor
+            out.write( ( new ZipShort( 20 ) ).getBytes() );
+
+            // bit3 set to signal, we use a data descriptor
+            out.write( ( new ZipShort( 8 ) ).getBytes() );
+        }
+        else
+        {
+            out.write( ( new ZipShort( 10 ) ).getBytes() );
+            out.write( ZERO );
+        }
+        m_written += 4;
+
+        // compression method
+        out.write( ( new ZipShort( entry.getMethod() ) ).getBytes() );
+        m_written += 2;
+
+        // last mod. time and date
+        out.write( toDosTime( new Date( entry.getTime() ) ).getBytes() );
+        m_written += 4;
+
+        // CRC
+        // compressed length
+        // uncompressed length
+        if( entry.getMethod() == DEFLATED )
+        {
+            out.write( LZERO );
+            out.write( LZERO );
+            out.write( LZERO );
+        }
+        else
+        {
+            out.write( ( new ZipLong( entry.getCrc() ) ).getBytes() );
+            out.write( ( new ZipLong( entry.getSize() ) ).getBytes() );
+            out.write( ( new ZipLong( entry.getSize() ) ).getBytes() );
+        }
+        m_written += 12;
+
+        // file name length
+        byte[] name = getBytes( entry.getName() );
+        out.write( ( new ZipShort( name.length ) ).getBytes() );
+        m_written += 2;
+
+        // extra field length
+        byte[] extra = entry.getLocalFileDataExtra();
+        out.write( ( new ZipShort( extra.length ) ).getBytes() );
+        m_written += 2;
+
+        // file name
+        out.write( name );
+        m_written += name.length;
+
+        // extra field
+        out.write( extra );
+        m_written += extra.length;
+
+        m_dataStart = m_written;
+    }
+
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipOutputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+/**
+ * Utility class that represents a two byte integer with conversion rules for
+ * the big endian byte order of ZIP files.
+ *
+ * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
+ * @version $Revision$
+ */
+public final class ZipShort implements Cloneable
+{
+    private int m_value;
+
+    /**
+     * Create instance from a number.
+     *
+     * @param value Description of Parameter
+     * @since 1.1
+     */
+    public ZipShort( int value )
+    {
+        this.m_value = value;
+    }
+
+    /**
+     * Create instance from bytes.
+     *
+     * @param bytes Description of Parameter
+     * @since 1.1
+     */
+    public ZipShort( byte[] bytes )
+    {
+        this( bytes, 0 );
+    }
+
+    /**
+     * Create instance from the two bytes starting at offset.
+     *
+     * @param bytes Description of Parameter
+     * @param offset Description of Parameter
+     * @since 1.1
+     */
+    public ZipShort( byte[] bytes, int offset )
+    {
+        m_value = ( bytes[ offset + 1 ] << 8 ) & 0xFF00;
+        m_value += ( bytes[ offset ] & 0xFF );
+    }
+
+    /**
+     * Get value as two bytes in big endian byte order.
+     *
+     * @return The Bytes value
+     * @since 1.1
+     */
+    public byte[] getBytes()
+    {
+        byte[] result = new byte[ 2 ];
+        result[ 0 ] = (byte)( m_value & 0xFF );
+        result[ 1 ] = (byte)( ( m_value & 0xFF00 ) >> 8 );
+        return result;
+    }
+
+    /**
+     * Get value as Java int.
+     *
+     * @return The Value value
+     * @since 1.1
+     */
+    public int getValue()
+    {
+        return m_value;
+    }
+
+    /**
+     * Override to make two instances with same value equal.
+     *
+     * @param o Description of Parameter
+     * @return Description of the Returned Value
+     * @since 1.1
+     */
+    public boolean equals( Object o )
+    {
+        if( o == null || !( o instanceof ZipShort ) )
+        {
+            return false;
+        }
+        return m_value == ( (ZipShort)o ).getValue();
+    }
+
+    /**
+     * Override to make two instances with same value equal.
+     *
+     * @return Description of the Returned Value
+     * @since 1.1
+     */
+    public int hashCode()
+    {
+        return m_value;
+    }
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,15 @@
+/**
+ * 
+ */
+package org.apache.commons.compress.changes;
+
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+
+/**
+ * @author Cy
+ *
+ */
+interface Change {
+	// public void perform(ArchiveInputStream input);
+	public int type();
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/Change.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.changes;
+
+import java.io.InputStream;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+
+
+public final class ChangeSet {
+
+	private final Set changes = new LinkedHashSet();
+	
+	public static final int CHANGE_TYPE_DELETE = 1;
+	public static final int CHANGE_TYPE_ADD = 2;
+	
+
+	public void delete( final String pFilename ) {
+		changes.add(new DeleteChange(pFilename));
+	}
+
+	public void move( final String pFrom, final String pTo ) {
+	}
+	
+	public void add( final ArchiveEntry pEntry, final InputStream pInput) {
+	}
+	
+	public Set asSet() {
+		return changes;
+	}
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeSet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.changes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+import org.apache.commons.compress.utils.IOUtils;
+
+/**
+ * Performs the operations of a change set
+ */
+public class ChangeWorker {
+	private ChangeWorker() {
+		// nothing to do
+	}
+	
+	/**
+	 * TODO
+	 * @param changes
+	 * @param in
+	 * @param out
+	 * @throws IOException 
+	 */
+	public static void perform(ChangeSet changes, ArchiveInputStream in, ArchiveOutputStream out) throws IOException {
+		ArchiveEntry entry = null;	
+		while((entry = in.getNextEntry()) != null) {
+			System.out.println(entry.getName());
+			boolean copy = true; 
+			
+			for (Iterator it = changes.asSet().iterator(); it.hasNext();) {
+				Change change = (Change)it.next();
+				
+				if(change.type() == ChangeSet.CHANGE_TYPE_DELETE) {
+					DeleteChange delete = ((DeleteChange)change);
+					if(entry.getName() != null &&
+					   entry.getName().equals(delete.targetFile())) {
+						copy = false;
+					}
+				}
+			}
+			
+			if(copy) {
+				// copy archive
+				// TODO: unsafe long to int 
+				System.out.println("Copy: " + entry.getName());
+				long size = entry.getSize();
+				out.putArchiveEntry(entry);
+				IOUtils.copy((InputStream)in, out, (int)size);
+				out.closeArchiveEntry();
+			}
+			
+			
+			System.out.println("---");
+		}
+		// add operation stuff
+		out.close();
+	}
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/ChangeWorker.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,37 @@
+/**
+ * 
+ */
+package org.apache.commons.compress.changes;
+
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+
+/**
+ * Implementation for a delete operation
+ */
+class DeleteChange implements Change {
+	private String filename = null;
+	
+	/**
+	 * Constructor. Takes the filename of the file to be deleted
+	 * from the stream as argument.
+	 * @param pFilename the filename of the file to delete
+	 */
+	public DeleteChange(final String pFilename) {
+		if(pFilename == null) {
+			throw new NullPointerException();
+		}
+		filename = pFilename;
+	}
+	
+	public void perform(ArchiveInputStream input) {
+		System.out.println("PERFORMING DELETE");
+	}
+
+	public String targetFile() {
+		return filename;
+	}
+	
+	public int type() {
+		return ChangeSet.CHANGE_TYPE_DELETE;
+	}
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/changes/DeleteChange.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,28 @@
+/**
+ * 
+ */
+package org.apache.commons.compress.compressors;
+
+/**
+ *
+ */
+public class CompressorException extends Exception {
+	/* Serial */
+	private static final long serialVersionUID = -2770299103090672278L;
+
+	public CompressorException() {
+		super();
+	}
+
+	public CompressorException(String arg0, Throwable arg1) {
+		super(arg0, arg1);
+	}
+
+	public CompressorException(String arg0) {
+		super(arg0);
+	}
+
+	public CompressorException(Throwable arg0) {
+		super(arg0);
+	}
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.compressors;
+
+import java.io.InputStream;
+
+public abstract class CompressorInputStream extends InputStream {
+	// TODO 
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorInputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.compressors;
+
+import java.io.OutputStream;
+
+
+public abstract class CompressorOutputStream extends OutputStream {
+	// TODO
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorOutputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,97 @@
+package org.apache.commons.compress.compressors;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
+
+public class CompressorStreamFactory {
+	final Map inputStreamClasses = new HashMap();
+	final Map outputStreamClasses = new HashMap();
+	
+	public CompressorStreamFactory() throws CompressorException {
+		registerInputStream("gz", GzipCompressorInputStream.class);
+		registerOutputStream("gz", GzipCompressorOutputStream.class);
+		registerInputStream("bzip2", BZip2CompressorInputStream.class);
+		registerOutputStream("bzip2", BZip2CompressorOutputStream.class);
+		
+	}
+	
+	public void registerInputStream( final String name, final Class stream ) throws CompressorException {
+		if (CompressorInputStream.class.isAssignableFrom(stream) && !(stream.isInterface())) {
+			inputStreamClasses.put(name, stream);
+        } else {
+            throw new CompressorException("Compressor does not implement the CompressorInputStream interface.");
+        }	
+	}
+
+	public void registerOutputStream( final String name, final Class stream ) throws CompressorException {
+		if (CompressorOutputStream.class.isAssignableFrom(stream) && !(stream.isInterface())) {
+			outputStreamClasses.put(name, stream);
+        } else {
+            throw new CompressorException("Compressor does not implement the CompressorOutputStream interface.");
+        }
+	}
+	
+	public CompressorInputStream createCompressorInputStream( final String name, final InputStream out ) throws CompressorException {
+        try {
+            final Class clazz = (Class) inputStreamClasses.get(name);
+
+            if (clazz == null) {
+            	throw new CompressorException("CompressorFactory could not create instance");
+            }
+
+            final Class[] params = { InputStream.class };
+            final Constructor constructor = clazz.getConstructor(params);
+            final Object[] initargs = { out };
+            return (CompressorInputStream) constructor.newInstance(initargs);
+        } catch (InstantiationException e) {
+            throw new CompressorException("CompressorFactory could not create instance", e);
+        } catch (IllegalAccessException e) {
+            throw new CompressorException("CompressorFactory could not create instance", e);
+        } catch (SecurityException e) {
+            throw new CompressorException("CompressorFactory could not create instance", e);
+        } catch (NoSuchMethodException e) {
+            throw new CompressorException("CompressorFactory could not create instance", e);
+        } catch (IllegalArgumentException e) {
+            throw new CompressorException("CompressorFactory could not create instance", e);
+        } catch (InvocationTargetException e) {
+            throw new CompressorException("CompressorFactory could not create instance", e);
+        }
+    }
+
+    public CompressorOutputStream createCompressorOutputStream( final String name, final OutputStream out ) throws ArchiveException {
+        try {
+            final Class clazz = (Class) outputStreamClasses.get(name);
+            
+            if (clazz == null) {
+            	throw new ArchiveException("CompressorFactory could not create instance");
+            }
+            
+            final Class[] params = { OutputStream.class };
+            final Constructor constructor = clazz.getConstructor(params);
+            final Object[] initargs = { out };
+            return (CompressorOutputStream) constructor.newInstance(initargs);
+        } catch (InstantiationException e) {
+            throw new ArchiveException("CompressorFactory could not create instance", e);
+        } catch (IllegalAccessException e) {
+            throw new ArchiveException("CompressorFactory could not create instance", e);
+        } catch (SecurityException e) {
+            throw new ArchiveException("CompressorFactory could not create instance", e);
+        } catch (NoSuchMethodException e) {
+            throw new ArchiveException("CompressorFactory could not create instance", e);
+        } catch (IllegalArgumentException e) {
+            throw new ArchiveException("CompressorFactory could not create instance", e);
+        } catch (InvocationTargetException e) {
+            throw new ArchiveException("CompressorFactory could not create instance", e);
+        }
+    }
+}

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java?rev=675498&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java Thu Jul 10 03:17:44 2008
@@ -0,0 +1,841 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.compressors.bzip2;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <ke...@aftexsw.com> to whom the Ant project is very grateful for his great
+ * code. 
+ */
+
+/**
+ * An input stream that decompresses from the BZip2 format (without the file
+ * header chars) to be read as any other stream.
+ * 
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ */
+public class BZip2CompressorInputStream extends CompressorInputStream implements BZip2Constants {
+
+    private static void cadvise() {
+        System.out.println("CRC Error");
+        //throw new CCoruptionError();
+    }
+
+    private static void badBGLengths() {
+        cadvise();
+    }
+
+    private static void bitStreamEOF() {
+        cadvise();
+    }
+
+    private static void compressedStreamEOF() {
+        cadvise();
+    }
+
+    private void makeMaps() {
+        int i;
+        nInUse = 0;
+        for (i = 0; i < 256; i++) {
+            if (inUse[i]) {
+                seqToUnseq[nInUse] = (char) i;
+                unseqToSeq[i] = (char) nInUse;
+                nInUse++;
+            }
+        }
+    }
+
+    /*
+      index of the last char in the block, so
+      the block size == last + 1.
+    */
+    private int  last;
+
+    /*
+      index in zptr[] of original string after sorting.
+    */
+    private int  origPtr;
+
+    /*
+      always: in the range 0 .. 9.
+      The current block size is 100000 * this number.
+    */
+    private int blockSize100k;
+
+    private boolean blockRandomised;
+
+    private int bsBuff;
+    private int bsLive;
+    private CRC mCrc = new CRC();
+
+    private boolean[] inUse = new boolean[256];
+    private int nInUse;
+
+    private char[] seqToUnseq = new char[256];
+    private char[] unseqToSeq = new char[256];
+
+    private char[] selector = new char[MAX_SELECTORS];
+    private char[] selectorMtf = new char[MAX_SELECTORS];
+
+    private int[] tt;
+    private char[] ll8;
+
+    /*
+      freq table collected to save a pass over the data
+      during decompression.
+    */
+    private int[] unzftab = new int[256];
+
+    private int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[] minLens = new int[N_GROUPS];
+
+    private InputStream bsStream;
+
+    private boolean streamEnd = false;
+
+    private int currentChar = -1;
+
+    private static final int START_BLOCK_STATE = 1;
+    private static final int RAND_PART_A_STATE = 2;
+    private static final int RAND_PART_B_STATE = 3;
+    private static final int RAND_PART_C_STATE = 4;
+    private static final int NO_RAND_PART_A_STATE = 5;
+    private static final int NO_RAND_PART_B_STATE = 6;
+    private static final int NO_RAND_PART_C_STATE = 7;
+
+    private int currentState = START_BLOCK_STATE;
+
+    private int storedBlockCRC, storedCombinedCRC;
+    private int computedBlockCRC, computedCombinedCRC;
+
+    int i2, count, chPrev, ch2;
+    int i, tPos;
+    int rNToGo = 0;
+    int rTPos  = 0;
+    int j2;
+    char z;
+
+    public BZip2CompressorInputStream(InputStream zStream) {
+        ll8 = null;
+        tt = null;
+        bsSetStream(zStream);
+        initialize();
+        initBlock();
+        setupBlock();
+    }
+
+    public int read() {
+        if (streamEnd) {
+            return -1;
+        } else {
+            int retChar = currentChar;
+            switch(currentState) {
+            case START_BLOCK_STATE:
+                break;
+            case RAND_PART_A_STATE:
+                break;
+            case RAND_PART_B_STATE:
+                setupRandPartB();
+                break;
+            case RAND_PART_C_STATE:
+                setupRandPartC();
+                break;
+            case NO_RAND_PART_A_STATE:
+                break;
+            case NO_RAND_PART_B_STATE:
+                setupNoRandPartB();
+                break;
+            case NO_RAND_PART_C_STATE:
+                setupNoRandPartC();
+                break;
+            default:
+                break;
+            }
+            return retChar;
+        }
+    }
+
+    private void initialize() {
+        char magic3, magic4;
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        if (magic3 != 'h' || magic4 < '1' || magic4 > '9') {
+            bsFinishedWithStream();
+            streamEnd = true;
+            return;
+        }
+
+        setDecompressStructureSizes(magic4 - '0');
+        computedCombinedCRC = 0;
+    }
+
+    private void initBlock() {
+        char magic1, magic2, magic3, magic4;
+        char magic5, magic6;
+        magic1 = bsGetUChar();
+        magic2 = bsGetUChar();
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        magic5 = bsGetUChar();
+        magic6 = bsGetUChar();
+        if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45
+            && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) {
+            complete();
+            return;
+        }
+
+        if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59
+            || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) {
+            badBlockHeader();
+            streamEnd = true;
+            return;
+        }
+
+        storedBlockCRC = bsGetInt32();
+
+        if (bsR(1) == 1) {
+            blockRandomised = true;
+        } else {
+            blockRandomised = false;
+        }
+
+        //        currBlockNo++;
+        getAndMoveToFrontDecode();
+
+        mCrc.initialiseCRC();
+        currentState = START_BLOCK_STATE;
+    }
+
+    private void endBlock() {
+        computedBlockCRC = mCrc.getFinalCRC();
+        /* A bad CRC is considered a fatal error. */
+        if (storedBlockCRC != computedBlockCRC) {
+            crcError();
+        }
+
+        computedCombinedCRC = (computedCombinedCRC << 1)
+            | (computedCombinedCRC >>> 31);
+        computedCombinedCRC ^= computedBlockCRC;
+    }
+
+    private void complete() {
+        storedCombinedCRC = bsGetInt32();
+        if (storedCombinedCRC != computedCombinedCRC) {
+            crcError();
+        }
+
+        bsFinishedWithStream();
+        streamEnd = true;
+    }
+
+    private static void blockOverrun() {
+        cadvise();
+    }
+
+    private static void badBlockHeader() {
+        cadvise();
+    }
+
+    private static void crcError() {
+        cadvise();
+    }
+
+    private void bsFinishedWithStream() {
+        try {
+            if (this.bsStream != null) {
+                if (this.bsStream != System.in) {
+                    this.bsStream.close();
+                    this.bsStream = null;
+                }
+            }
+        } catch (IOException ioe) {
+            //ignore
+        }
+    }
+
+    private void bsSetStream(InputStream f) {
+        bsStream = f;
+        bsLive = 0;
+        bsBuff = 0;
+    }
+
+    private int bsR(int n) {
+        int v;
+        while (bsLive < n) {
+            int zzi;
+            char thech = 0;
+            try {
+                thech = (char) bsStream.read();
+            } catch (IOException e) {
+                compressedStreamEOF();
+            }
+            if (thech == -1) {
+                compressedStreamEOF();
+            }
+            zzi = thech;
+            bsBuff = (bsBuff << 8) | (zzi & 0xff);
+            bsLive += 8;
+        }
+
+        v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1);
+        bsLive -= n;
+        return v;
+    }
+
+    private char bsGetUChar() {
+        return (char) bsR(8);
+    }
+
+    private int bsGetint() {
+        int u = 0;
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        return u;
+    }
+
+    private int bsGetIntVS(int numBits) {
+        return (int) bsR(numBits);
+    }
+
+    private int bsGetInt32() {
+        return (int) bsGetint();
+    }
+
+    private void hbCreateDecodeTables(int[] limit, int[] base,
+                                      int[] perm, char[] length,
+                                      int minLen, int maxLen, int alphaSize) {
+        int pp, i, j, vec;
+
+        pp = 0;
+        for (i = minLen; i <= maxLen; i++) {
+            for (j = 0; j < alphaSize; j++) {
+                if (length[j] == i) {
+                    perm[pp] = j;
+                    pp++;
+                }
+            }
+        }
+
+        for (i = 0; i < MAX_CODE_LEN; i++) {
+            base[i] = 0;
+        }
+        for (i = 0; i < alphaSize; i++) {
+            base[length[i] + 1]++;
+        }
+
+        for (i = 1; i < MAX_CODE_LEN; i++) {
+            base[i] += base[i - 1];
+        }
+
+        for (i = 0; i < MAX_CODE_LEN; i++) {
+            limit[i] = 0;
+        }
+        vec = 0;
+
+        for (i = minLen; i <= maxLen; i++) {
+            vec += (base[i + 1] - base[i]);
+            limit[i] = vec - 1;
+            vec <<= 1;
+        }
+        for (i = minLen + 1; i <= maxLen; i++) {
+            base[i] = ((limit[i - 1] + 1) << 1) - base[i];
+        }
+    }
+
+    private void recvDecodingTables() {
+        char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE];
+        int i, j, t, nGroups, nSelectors, alphaSize;
+        int minLen, maxLen;
+        boolean[] inUse16 = new boolean[16];
+
+        /* Receive the mapping table */
+        for (i = 0; i < 16; i++) {
+            if (bsR(1) == 1) {
+                inUse16[i] = true;
+            } else {
+                inUse16[i] = false;
+            }
+        }
+
+        for (i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        for (i = 0; i < 16; i++) {
+            if (inUse16[i]) {
+                for (j = 0; j < 16; j++) {
+                    if (bsR(1) == 1) {
+                        inUse[i * 16 + j] = true;
+                    }
+                }
+            }
+        }
+
+        makeMaps();
+        alphaSize = nInUse + 2;
+
+        /* Now the selectors */
+        nGroups = bsR(3);
+        nSelectors = bsR(15);
+        for (i = 0; i < nSelectors; i++) {
+            j = 0;
+            while (bsR(1) == 1) {
+                j++;
+            }
+            selectorMtf[i] = (char) j;
+        }
+
+        /* Undo the MTF values for the selectors. */
+        {
+            char[] pos = new char[N_GROUPS];
+            char tmp, v;
+            for (v = 0; v < nGroups; v++) {
+                pos[v] = v;
+            }
+
+            for (i = 0; i < nSelectors; i++) {
+                v = selectorMtf[i];
+                tmp = pos[v];
+                while (v > 0) {
+                    pos[v] = pos[v - 1];
+                    v--;
+                }
+                pos[0] = tmp;
+                selector[i] = tmp;
+            }
+        }
+
+        /* Now the coding tables */
+        for (t = 0; t < nGroups; t++) {
+            int curr = bsR(5);
+            for (i = 0; i < alphaSize; i++) {
+                while (bsR(1) == 1) {
+                    if (bsR(1) == 0) {
+                        curr++;
+                    } else {
+                        curr--;
+                    }
+                }
+                len[t][i] = (char) curr;
+            }
+        }
+
+        /* Create the Huffman decoding tables */
+        for (t = 0; t < nGroups; t++) {
+            minLen = 32;
+            maxLen = 0;
+            for (i = 0; i < alphaSize; i++) {
+                if (len[t][i] > maxLen) {
+                    maxLen = len[t][i];
+                }
+                if (len[t][i] < minLen) {
+                    minLen = len[t][i];
+                }
+            }
+            hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
+                                 maxLen, alphaSize);
+            minLens[t] = minLen;
+        }
+    }
+
+    private void getAndMoveToFrontDecode() {
+        char[] yy = new char[256];
+        int i, j, nextSym, limitLast;
+        int EOB, groupNo, groupPos;
+
+        limitLast = baseBlockSize * blockSize100k;
+        origPtr = bsGetIntVS(24);
+
+        recvDecodingTables();
+        EOB = nInUse + 1;
+        groupNo = -1;
+        groupPos = 0;
+
+        /*
+          Setting up the unzftab entries here is not strictly
+          necessary, but it does save having to do it later
+          in a separate pass, and so saves a block's worth of
+          cache misses.
+        */
+        for (i = 0; i <= 255; i++) {
+            unzftab[i] = 0;
+        }
+
+        for (i = 0; i <= 255; i++) {
+            yy[i] = (char) i;
+        }
+
+        last = -1;
+
+        {
+            int zt, zn, zvec, zj;
+            if (groupPos == 0) {
+                groupNo++;
+                groupPos = G_SIZE;
+            }
+            groupPos--;
+            zt = selector[groupNo];
+            zn = minLens[zt];
+            zvec = bsR(zn);
+            while (zvec > limit[zt][zn]) {
+                zn++;
+                {
+                    {
+                        while (bsLive < 1) {
+                            int zzi;
+                            char thech = 0;
+                            try {
+                                thech = (char) bsStream.read();
+                            } catch (IOException e) {
+                                compressedStreamEOF();
+                            }
+                            if (thech == -1) {
+                                compressedStreamEOF();
+                            }
+                            zzi = thech;
+                            bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                            bsLive += 8;
+                        }
+                    }
+                    zj = (bsBuff >> (bsLive - 1)) & 1;
+                    bsLive--;
+                }
+                zvec = (zvec << 1) | zj;
+            }
+            nextSym = perm[zt][zvec - base[zt][zn]];
+        }
+
+        while (true) {
+
+            if (nextSym == EOB) {
+                break;
+            }
+
+            if (nextSym == RUNA || nextSym == RUNB) {
+                char ch;
+                int s = -1;
+                int N = 1;
+                do {
+                    if (nextSym == RUNA) {
+                        s = s + (0 + 1) * N;
+                    } else if (nextSym == RUNB) {
+                        s = s + (1 + 1) * N;
+                           }
+                    N = N * 2;
+                    {
+                        int zt, zn, zvec, zj;
+                        if (groupPos == 0) {
+                            groupNo++;
+                            groupPos = G_SIZE;
+                        }
+                        groupPos--;
+                        zt = selector[groupNo];
+                        zn = minLens[zt];
+                        zvec = bsR(zn);
+                        while (zvec > limit[zt][zn]) {
+                            zn++;
+                            {
+                                {
+                                    while (bsLive < 1) {
+                                        int zzi;
+                                        char thech = 0;
+                                        try {
+                                            thech = (char) bsStream.read();
+                                        } catch (IOException e) {
+                                            compressedStreamEOF();
+                                        }
+                                        if (thech == -1) {
+                                            compressedStreamEOF();
+                                        }
+                                        zzi = thech;
+                                        bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                                        bsLive += 8;
+                                    }
+                                }
+                                zj = (bsBuff >> (bsLive - 1)) & 1;
+                                bsLive--;
+                            }
+                            zvec = (zvec << 1) | zj;
+                        }
+                        nextSym = perm[zt][zvec - base[zt][zn]];
+                    }
+                } while (nextSym == RUNA || nextSym == RUNB);
+
+                s++;
+                ch = seqToUnseq[yy[0]];
+                unzftab[ch] += s;
+
+                while (s > 0) {
+                    last++;
+                    ll8[last] = ch;
+                    s--;
+                }
+
+                if (last >= limitLast) {
+                    blockOverrun();
+                }
+                continue;
+            } else {
+                char tmp;
+                last++;
+                if (last >= limitLast) {
+                    blockOverrun();
+                }
+
+                tmp = yy[nextSym - 1];
+                unzftab[seqToUnseq[tmp]]++;
+                ll8[last] = seqToUnseq[tmp];
+
+                /*
+                  This loop is hammered during decompression,
+                  hence the unrolling.
+
+                  for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1];
+                */
+
+                j = nextSym - 1;
+                for (; j > 3; j -= 4) {
+                    yy[j]     = yy[j - 1];
+                    yy[j - 1] = yy[j - 2];
+                    yy[j - 2] = yy[j - 3];
+                    yy[j - 3] = yy[j - 4];
+                }
+                for (; j > 0; j--) {
+                    yy[j] = yy[j - 1];
+                }
+
+                yy[0] = tmp;
+                {
+                    int zt, zn, zvec, zj;
+                    if (groupPos == 0) {
+                        groupNo++;
+                        groupPos = G_SIZE;
+                    }
+                    groupPos--;
+                    zt = selector[groupNo];
+                    zn = minLens[zt];
+                    zvec = bsR(zn);
+                    while (zvec > limit[zt][zn]) {
+                        zn++;
+                        {
+                            {
+                                while (bsLive < 1) {
+                                    int zzi;
+                                    char thech = 0;
+                                    try {
+                                        thech = (char) bsStream.read();
+                                    } catch (IOException e) {
+                                        compressedStreamEOF();
+                                    }
+                                    zzi = thech;
+                                    bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                                    bsLive += 8;
+                                }
+                            }
+                            zj = (bsBuff >> (bsLive - 1)) & 1;
+                            bsLive--;
+                        }
+                        zvec = (zvec << 1) | zj;
+                    }
+                    nextSym = perm[zt][zvec - base[zt][zn]];
+                }
+                continue;
+            }
+        }
+    }
+
+    private void setupBlock() {
+        int[] cftab = new int[257];
+        char ch;
+
+        cftab[0] = 0;
+        for (i = 1; i <= 256; i++) {
+            cftab[i] = unzftab[i - 1];
+        }
+        for (i = 1; i <= 256; i++) {
+            cftab[i] += cftab[i - 1];
+        }
+
+        for (i = 0; i <= last; i++) {
+            ch = (char) ll8[i];
+            tt[cftab[ch]] = i;
+            cftab[ch]++;
+        }
+        cftab = null;
+
+        tPos = tt[origPtr];
+
+        count = 0;
+        i2 = 0;
+        ch2 = 256;   /* not a char and not EOF */
+
+        if (blockRandomised) {
+            rNToGo = 0;
+            rTPos = 0;
+            setupRandPartA();
+        } else {
+            setupNoRandPartA();
+        }
+    }
+
+    private void setupRandPartA() {
+        if (i2 <= last) {
+            chPrev = ch2;
+            ch2 = ll8[tPos];
+            tPos = tt[tPos];
+            if (rNToGo == 0) {
+                rNToGo = rNums[rTPos];
+                rTPos++;
+                if (rTPos == 512) {
+                    rTPos = 0;
+                }
+            }
+            rNToGo--;
+            ch2 ^= (int) ((rNToGo == 1) ? 1 : 0);
+            i2++;
+
+            currentChar = ch2;
+            currentState = RAND_PART_B_STATE;
+            mCrc.updateCRC(ch2);
+        } else {
+            endBlock();
+            initBlock();
+            setupBlock();
+        }
+    }
+
+    private void setupNoRandPartA() {
+        if (i2 <= last) {
+            chPrev = ch2;
+            ch2 = ll8[tPos];
+            tPos = tt[tPos];
+            i2++;
+
+            currentChar = ch2;
+            currentState = NO_RAND_PART_B_STATE;
+            mCrc.updateCRC(ch2);
+        } else {
+            endBlock();
+            initBlock();
+            setupBlock();
+        }
+    }
+
+    private void setupRandPartB() {
+        if (ch2 != chPrev) {
+            currentState = RAND_PART_A_STATE;
+            count = 1;
+            setupRandPartA();
+        } else {
+            count++;
+            if (count >= 4) {
+                z = ll8[tPos];
+                tPos = tt[tPos];
+                if (rNToGo == 0) {
+                    rNToGo = rNums[rTPos];
+                    rTPos++;
+                    if (rTPos == 512) {
+                        rTPos = 0;
+                    }
+                }
+                rNToGo--;
+                z ^= ((rNToGo == 1) ? 1 : 0);
+                j2 = 0;
+                currentState = RAND_PART_C_STATE;
+                setupRandPartC();
+            } else {
+                currentState = RAND_PART_A_STATE;
+                setupRandPartA();
+            }
+        }
+    }
+
+    private void setupRandPartC() {
+        if (j2 < (int) z) {
+            currentChar = ch2;
+            mCrc.updateCRC(ch2);
+            j2++;
+        } else {
+            currentState = RAND_PART_A_STATE;
+            i2++;
+            count = 0;
+            setupRandPartA();
+        }
+    }
+
+    private void setupNoRandPartB() {
+        if (ch2 != chPrev) {
+            currentState = NO_RAND_PART_A_STATE;
+            count = 1;
+            setupNoRandPartA();
+        } else {
+            count++;
+            if (count >= 4) {
+                z = ll8[tPos];
+                tPos = tt[tPos];
+                currentState = NO_RAND_PART_C_STATE;
+                j2 = 0;
+                setupNoRandPartC();
+            } else {
+                currentState = NO_RAND_PART_A_STATE;
+                setupNoRandPartA();
+            }
+        }
+    }
+
+    private void setupNoRandPartC() {
+        if (j2 < (int) z) {
+            currentChar = ch2;
+            mCrc.updateCRC(ch2);
+            j2++;
+        } else {
+            currentState = NO_RAND_PART_A_STATE;
+            i2++;
+            count = 0;
+            setupNoRandPartA();
+        }
+    }
+
+    private void setDecompressStructureSizes(int newSize100k) {
+        if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k
+               && blockSize100k <= 9)) {
+            // throw new IOException("Invalid block size");
+        }
+
+        blockSize100k = newSize100k;
+
+        if (newSize100k == 0) {
+            return;
+        }
+
+        int n = baseBlockSize * newSize100k;
+        ll8 = new char[n];
+        tt = new int[n];
+    }
+}
+

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain