You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/08/22 13:19:45 UTC

[commons-compress] 01/01: Pack200 from Apache Harmony initially in its own package but should be repackaged.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch pack200
in repository https://gitbox.apache.org/repos/asf/commons-compress.git

commit 48351f2f610949b42cae1c2d818b21835bdcb0bd
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Aug 22 09:19:32 2020 -0400

    Pack200 from Apache Harmony initially in its own package but should be
    repackaged.
---
 pom.xml                                            |   25 +-
 .../pack200/Pack200CompressorInputStream.java      |    2 +-
 .../pack200/Pack200CompressorOutputStream.java     |    2 +-
 .../compress/compressors/pack200/Pack200Utils.java |    2 +-
 .../commons/compress/java/util/jar/Pack200.java    |  339 ++++
 .../harmony/archive/internal/nls/Messages.java     |  251 +++
 .../java/org/apache/harmony/pack200/Archive.java   |  332 ++++
 .../harmony/pack200/AttributeDefinitionBands.java  |  260 ++++
 .../java/org/apache/harmony/pack200/BHSDCodec.java |  516 +++++++
 .../java/org/apache/harmony/pack200/BandSet.java   |  788 ++++++++++
 .../java/org/apache/harmony/pack200/BcBands.java   |  583 +++++++
 .../java/org/apache/harmony/pack200/CPClass.java   |   57 +
 .../org/apache/harmony/pack200/CPConstant.java     |   26 +
 .../java/org/apache/harmony/pack200/CPDouble.java  |   37 +
 .../java/org/apache/harmony/pack200/CPFloat.java   |   37 +
 .../java/org/apache/harmony/pack200/CPInt.java     |   43 +
 .../java/org/apache/harmony/pack200/CPLong.java    |   48 +
 .../apache/harmony/pack200/CPMethodOrField.java    |   83 +
 .../org/apache/harmony/pack200/CPNameAndType.java  |   61 +
 .../org/apache/harmony/pack200/CPSignature.java    |   86 ++
 .../java/org/apache/harmony/pack200/CPString.java  |   44 +
 .../java/org/apache/harmony/pack200/CPUTF8.java    |   42 +
 .../harmony/pack200/CanonicalCodecFamilies.java    |  210 +++
 .../org/apache/harmony/pack200/ClassBands.java     | 1616 ++++++++++++++++++++
 .../java/org/apache/harmony/pack200/Codec.java     |  246 +++
 .../org/apache/harmony/pack200/CodecEncoding.java  |  346 +++++
 .../apache/harmony/pack200/ConstantPoolEntry.java  |   34 +
 .../java/org/apache/harmony/pack200/CpBands.java   |  686 +++++++++
 .../java/org/apache/harmony/pack200/FileBands.java |  183 +++
 .../java/org/apache/harmony/pack200/IcBands.java   |  199 +++
 .../java/org/apache/harmony/pack200/IntList.java   |  260 ++++
 .../apache/harmony/pack200/MetadataBandGroup.java  |  462 ++++++
 .../org/apache/harmony/pack200/NewAttribute.java   |  202 +++
 .../apache/harmony/pack200/NewAttributeBands.java  |  965 ++++++++++++
 .../org/apache/harmony/pack200/Pack200Adapter.java |   59 +
 .../apache/harmony/pack200/Pack200ClassReader.java |   81 +
 .../apache/harmony/pack200/Pack200Exception.java   |   36 +
 .../harmony/pack200/Pack200PackerAdapter.java      |  102 ++
 .../org/apache/harmony/pack200/PackingOptions.java |  363 +++++
 .../org/apache/harmony/pack200/PackingUtils.java   |  250 +++
 .../apache/harmony/pack200/PopulationCodec.java    |  170 ++
 .../java/org/apache/harmony/pack200/RunCodec.java  |  156 ++
 .../java/org/apache/harmony/pack200/Segment.java   |  707 +++++++++
 .../org/apache/harmony/pack200/SegmentHeader.java  |  360 +++++
 .../java/org/apache/harmony/unpack200/Archive.java |  248 +++
 .../harmony/unpack200/AttrDefinitionBands.java     |  121 ++
 .../apache/harmony/unpack200/AttributeLayout.java  |  245 +++
 .../harmony/unpack200/AttributeLayoutMap.java      |  284 ++++
 .../java/org/apache/harmony/unpack200/BandSet.java |  551 +++++++
 .../java/org/apache/harmony/unpack200/BcBands.java |  589 +++++++
 .../org/apache/harmony/unpack200/ClassBands.java   | 1421 +++++++++++++++++
 .../java/org/apache/harmony/unpack200/CpBands.java |  705 +++++++++
 .../org/apache/harmony/unpack200/FileBands.java    |  127 ++
 .../org/apache/harmony/unpack200/IMatcher.java     |   26 +
 .../java/org/apache/harmony/unpack200/IcBands.java |  249 +++
 .../java/org/apache/harmony/unpack200/IcTuple.java |  358 +++++
 .../harmony/unpack200/MetadataBandGroup.java       |  248 +++
 .../harmony/unpack200/NewAttributeBands.java       | 1050 +++++++++++++
 .../harmony/unpack200/Pack200UnpackerAdapter.java  |   74 +
 .../java/org/apache/harmony/unpack200/Segment.java |  639 ++++++++
 .../harmony/unpack200/SegmentConstantPool.java     |  317 ++++
 .../unpack200/SegmentConstantPoolArrayCache.java   |  182 +++
 .../apache/harmony/unpack200/SegmentHeader.java    |  438 ++++++
 .../apache/harmony/unpack200/SegmentOptions.java   |  126 ++
 .../org/apache/harmony/unpack200/SegmentUtils.java |  127 ++
 .../bytecode/AnnotationDefaultAttribute.java       |   73 +
 .../unpack200/bytecode/AnnotationsAttribute.java   |  197 +++
 .../harmony/unpack200/bytecode/Attribute.java      |  115 ++
 .../unpack200/bytecode/BCIRenumberedAttribute.java |   78 +
 .../harmony/unpack200/bytecode/ByteCode.java       |  370 +++++
 .../apache/harmony/unpack200/bytecode/CPClass.java |   96 ++
 .../harmony/unpack200/bytecode/CPConstant.java     |   71 +
 .../unpack200/bytecode/CPConstantNumber.java       |   32 +
 .../harmony/unpack200/bytecode/CPDouble.java       |   39 +
 .../apache/harmony/unpack200/bytecode/CPField.java |   34 +
 .../harmony/unpack200/bytecode/CPFieldRef.java     |   98 ++
 .../apache/harmony/unpack200/bytecode/CPFloat.java |   39 +
 .../harmony/unpack200/bytecode/CPInteger.java      |   39 +
 .../unpack200/bytecode/CPInterfaceMethodRef.java   |   54 +
 .../apache/harmony/unpack200/bytecode/CPLong.java  |   39 +
 .../harmony/unpack200/bytecode/CPMember.java       |  123 ++
 .../harmony/unpack200/bytecode/CPMethod.java       |   52 +
 .../harmony/unpack200/bytecode/CPMethodRef.java    |   51 +
 .../harmony/unpack200/bytecode/CPNameAndType.java  |  123 ++
 .../apache/harmony/unpack200/bytecode/CPRef.java   |  109 ++
 .../harmony/unpack200/bytecode/CPString.java       |   73 +
 .../apache/harmony/unpack200/bytecode/CPUTF8.java  |   90 ++
 .../unpack200/bytecode/ClassConstantPool.java      |  271 ++++
 .../harmony/unpack200/bytecode/ClassFile.java      |   73 +
 .../harmony/unpack200/bytecode/ClassFileEntry.java |   61 +
 .../harmony/unpack200/bytecode/CodeAttribute.java  |  191 +++
 .../unpack200/bytecode/ConstantPoolEntry.java      |   83 +
 .../unpack200/bytecode/ConstantValueAttribute.java |   90 ++
 .../unpack200/bytecode/DeprecatedAttribute.java    |   64 +
 .../bytecode/EnclosingMethodAttribute.java         |   91 ++
 .../unpack200/bytecode/ExceptionTableEntry.java    |   96 ++
 .../unpack200/bytecode/ExceptionsAttribute.java    |  114 ++
 .../unpack200/bytecode/InnerClassesAttribute.java  |  185 +++
 .../bytecode/LineNumberTableAttribute.java         |   91 ++
 .../bytecode/LocalVariableTableAttribute.java      |  167 ++
 .../bytecode/LocalVariableTypeTableAttribute.java  |  168 ++
 .../harmony/unpack200/bytecode/NewAttribute.java   |  246 +++
 .../harmony/unpack200/bytecode/OperandManager.java |  238 +++
 ...timeVisibleorInvisibleAnnotationsAttribute.java |   84 +
 ...leorInvisibleParameterAnnotationsAttribute.java |  126 ++
 .../unpack200/bytecode/SignatureAttribute.java     |   79 +
 .../unpack200/bytecode/SourceFileAttribute.java    |   92 ++
 .../unpack200/bytecode/forms/ByteCodeForm.java     |  541 +++++++
 .../harmony/unpack200/bytecode/forms/ByteForm.java |   42 +
 .../unpack200/bytecode/forms/ClassRefForm.java     |   71 +
 .../bytecode/forms/ClassSpecificReferenceForm.java |   55 +
 .../unpack200/bytecode/forms/DoubleForm.java       |   39 +
 .../unpack200/bytecode/forms/FieldRefForm.java     |   39 +
 .../unpack200/bytecode/forms/FloatRefForm.java     |   43 +
 .../unpack200/bytecode/forms/IMethodRefForm.java   |   56 +
 .../harmony/unpack200/bytecode/forms/IincForm.java |   45 +
 .../bytecode/forms/InitMethodReferenceForm.java    |   56 +
 .../unpack200/bytecode/forms/IntRefForm.java       |   44 +
 .../unpack200/bytecode/forms/LabelForm.java        |   80 +
 .../unpack200/bytecode/forms/LocalForm.java        |   44 +
 .../harmony/unpack200/bytecode/forms/LongForm.java |   39 +
 .../unpack200/bytecode/forms/LookupSwitchForm.java |  110 ++
 .../unpack200/bytecode/forms/MethodRefForm.java    |   41 +
 .../bytecode/forms/MultiANewArrayForm.java         |   51 +
 .../bytecode/forms/NarrowClassRefForm.java         |   49 +
 .../unpack200/bytecode/forms/NewClassRefForm.java  |   69 +
 .../bytecode/forms/NewInitMethodRefForm.java       |   50 +
 .../unpack200/bytecode/forms/NoArgumentForm.java   |   44 +
 .../unpack200/bytecode/forms/ReferenceForm.java    |   69 +
 .../unpack200/bytecode/forms/ShortForm.java        |   42 +
 .../bytecode/forms/SingleByteReferenceForm.java    |   52 +
 .../unpack200/bytecode/forms/StringRefForm.java    |   63 +
 .../bytecode/forms/SuperFieldRefForm.java          |   45 +
 .../bytecode/forms/SuperInitMethodRefForm.java     |   33 +
 .../bytecode/forms/SuperMethodRefForm.java         |   45 +
 .../unpack200/bytecode/forms/SwitchForm.java       |   57 +
 .../unpack200/bytecode/forms/TableSwitchForm.java  |  113 ++
 .../unpack200/bytecode/forms/ThisFieldRefForm.java |   45 +
 .../bytecode/forms/ThisInitMethodRefForm.java      |   34 +
 .../bytecode/forms/ThisMethodRefForm.java          |   45 +
 .../bytecode/forms/VariableInstructionForm.java    |  126 ++
 .../harmony/unpack200/bytecode/forms/WideForm.java |  148 ++
 142 files changed, 26602 insertions(+), 10 deletions(-)

diff --git a/pom.xml b/pom.xml
index 0a58bb0..0940702 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
       org.tukaani.xz;resolution:=optional,
       org.brotli.dec;resolution:=optional,
       com.github.luben.zstd;resolution:=optional,
+      org.objectweb.asm;resolution:=optional,
       javax.crypto.*;resolution:=optional,
       *
     </commons.osgi.import>
@@ -87,12 +88,6 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
 
   <dependencies>
     <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>4.13</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>com.github.luben</groupId>
       <artifactId>zstd-jni</artifactId>
       <version>1.4.5-6</version>
@@ -110,6 +105,22 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
       <version>1.8</version>
       <optional>true</optional>
     </dependency>
+
+    <!-- Pack200 -->
+    <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>3.2</version>
+      <optional>true</optional>
+    </dependency>
+
+    <!--  Test -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
@@ -123,7 +134,7 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
       <scope>test</scope>
     </dependency>
 
-    <!-- integration test verifiying OSGi bundle works -->
+    <!-- integration test verifying OSGi bundle works -->
     <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam-container-native</artifactId>
diff --git a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java
index 1d849a3..0336fed 100644
--- a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java
+++ b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorInputStream.java
@@ -24,7 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.Map;
 import java.util.jar.JarOutputStream;
-import java.util.jar.Pack200;
+import org.apache.commons.compress.java.util.jar.Pack200;
 
 import org.apache.commons.compress.compressors.CompressorInputStream;
 import org.apache.commons.compress.utils.CloseShieldFilterInputStream;
diff --git a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java
index e5be87b..fea31f4 100644
--- a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200CompressorOutputStream.java
@@ -23,7 +23,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Map;
 import java.util.jar.JarInputStream;
-import java.util.jar.Pack200;
+import org.apache.commons.compress.java.util.jar.Pack200;
 
 import org.apache.commons.compress.compressors.CompressorOutputStream;
 
diff --git a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java
index df07b86..c57bb1f 100644
--- a/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java
+++ b/src/main/java/org/apache/commons/compress/compressors/pack200/Pack200Utils.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
-import java.util.jar.Pack200;
+import org.apache.commons.compress.java.util.jar.Pack200;
 
 /**
  * Utility methods for Pack200.
diff --git a/src/main/java/org/apache/commons/compress/java/util/jar/Pack200.java b/src/main/java/org/apache/commons/compress/java/util/jar/Pack200.java
new file mode 100644
index 0000000..998c7bf
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/java/util/jar/Pack200.java
@@ -0,0 +1,339 @@
+/*
+ * 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.java.util.jar;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.SortedMap;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import org.apache.harmony.archive.internal.nls.Messages;
+
+/**
+ * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}.
+ */
+public abstract class Pack200 {
+
+    private static final String SYSTEM_PROPERTY_PACKER = "java.util.jar.Pack200.Packer"; //$NON-NLS-1$
+
+    private static final String SYSTEM_PROPERTY_UNPACKER = "java.util.jar.Pack200.Unpacker"; //$NON-NLS-1$
+
+    /**
+     * Prevent this class from being instantiated.
+     */
+    private Pack200() {
+        // do nothing
+    }
+
+    /**
+     * Returns a new instance of a packer engine.
+     * <p>
+     * The implementation of the packer engine is defined by the system property
+     * {@code 'java.util.jar.Pack200.Packer'}. If this system property is
+     * defined an instance of the specified class is returned, otherwise the
+     * system's default implementation is returned.
+     *
+     * @return an instance of {@code Packer}
+     */
+    public static Pack200.Packer newPacker() {
+        return (Packer) AccessController
+                .doPrivileged(new PrivilegedAction<Object>() {
+                    public Object run() {
+                        String className = System
+                                .getProperty(SYSTEM_PROPERTY_PACKER,
+                                        "org.apache.harmony.pack200.Pack200PackerAdapter"); //$NON-NLS-1$
+                        try {
+                            // TODO Not sure if this will cause problems with
+                            // loading the packer
+                            return ClassLoader.getSystemClassLoader()
+                                    .loadClass(className).newInstance();
+                        } catch (Exception e) {
+                            throw new Error(Messages.getString("archive.3E",className), e); //$NON-NLS-1$
+                        }
+                    }
+                });
+
+    }
+
+    /**
+     * Returns a new instance of a unpacker engine.
+     * <p>
+     * The implementation of the unpacker engine is defined by the system
+     * property {@code 'java.util.jar.Pack200.Unpacker'}. If this system
+     * property is defined an instance of the specified class is returned,
+     * otherwise the system's default implementation is returned.
+     *
+     * @return a instance of {@code Unpacker}.
+     */
+    public static Pack200.Unpacker newUnpacker() {
+        return (Unpacker) AccessController
+                .doPrivileged(new PrivilegedAction<Object>() {
+                    public Object run() {
+                        String className = System
+                                .getProperty(SYSTEM_PROPERTY_UNPACKER,
+                                        "org.apache.harmony.unpack200.Pack200UnpackerAdapter");//$NON-NLS-1$
+                        try {
+                            return ClassLoader.getSystemClassLoader()
+                                    .loadClass(className).newInstance();
+                        } catch (Exception e) {
+                            throw new Error(Messages.getString("archive.3E",className), e); //$NON-NLS-1$
+                        }
+                    }
+                });
+    }
+
+    /**
+     * The interface defining the API for converting a JAR file to an output
+     * stream in the Pack200 format.
+     */
+    public static interface Packer {
+
+        /**
+         * the format of a class attribute name.
+         */
+        static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$
+
+        /**
+         * the format of a code attribute name.
+         */
+        static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$
+
+        /**
+         * the deflation hint to set in the output archive.
+         */
+        static final String DEFLATE_HINT = "pack.deflate.hint";//$NON-NLS-1$
+
+        /**
+         * the indicated amount of effort to use in compressing the archive.
+         */
+        static final String EFFORT = "pack.effort";//$NON-NLS-1$
+
+        /**
+         * a String representation for {@code error}.
+         */
+        static final String ERROR = "error";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code false}.
+         */
+        static final String FALSE = "false";//$NON-NLS-1$
+
+        /**
+         * the format of a field attribute name.
+         */
+        static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";//$NON-NLS-1$
+
+        /**
+         * a String representation for {@code keep}.
+         */
+        static final String KEEP = "keep";//$NON-NLS-1$
+
+        /**
+         * decide if all elements shall transmit in their original order.
+         */
+        static final String KEEP_FILE_ORDER = "pack.keep.file.order";//$NON-NLS-1$
+
+        /**
+         * a String representation for {@code latest}.
+         */
+        static final String LATEST = "latest";//$NON-NLS-1$
+
+        /**
+         * the format of a method attribute name.
+         */
+        static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";//$NON-NLS-1$
+
+        /**
+         * if it shall attempt to determine the latest modification time if this
+         * is set to {@code LATEST}.
+         */
+        static final String MODIFICATION_TIME = "pack.modification.time";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code pass}.
+         */
+        static final String PASS = "pass";//$NON-NLS-1$
+
+        /**
+         * the file that will not be compressed.
+         */
+        static final String PASS_FILE_PFX = "pack.pass.file.";//$NON-NLS-1$
+
+        /**
+         * packer progress as a percentage.
+         */
+        static final String PROGRESS = "pack.progress";//$NON-NLS-1$
+
+        /**
+         * The number of bytes of each archive segment.
+         */
+        static final String SEGMENT_LIMIT = "pack.segment.limit";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code strip}.
+         */
+        static final String STRIP = "strip";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code true}.
+         */
+        static final String TRUE = "true";//$NON-NLS-1$
+
+        /**
+         * the action to take if an unknown attribute is encountered.
+         */
+        static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";//$NON-NLS-1$
+
+        /**
+         * Returns a sorted map of the properties of this packer.
+         *
+         * @return the properties of the packer.
+         */
+        SortedMap<String, String> properties();
+
+        /**
+         * Pack the specified JAR file to the specified output stream.
+         *
+         * @param in
+         *            JAR file to be compressed.
+         * @param out
+         *            stream of compressed data.
+         * @throws IOException
+         *             if I/O exception occurs.
+         */
+        void pack(JarFile in, OutputStream out) throws IOException;
+
+        /**
+         * Pack the data from the specified jar input stream to the specified
+         * output stream.
+         *
+         * @param in
+         *            stream of uncompressed JAR data.
+         * @param out
+         *            stream of compressed data.
+         * @throws IOException
+         *             if I/O exception occurs.
+         */
+        void pack(JarInputStream in, OutputStream out) throws IOException;
+
+        /**
+         * add a listener for PropertyChange events
+         *
+         * @param listener
+         *            the listener to listen if PropertyChange events occurs
+         */
+        void addPropertyChangeListener(PropertyChangeListener listener);
+
+        /**
+         * remove a listener
+         *
+         * @param listener
+         *            listener to remove
+         */
+        void removePropertyChangeListener(PropertyChangeListener listener);
+    }
+
+    /**
+     * The interface defining the API for converting a packed stream in the
+     * Pack200 format to a JAR file.
+     */
+    public static interface Unpacker {
+
+        /**
+         * The String indicating if the unpacker should ignore all transmitted
+         * values,can be replaced by either {@code true} or {@code false}.
+         */
+        static final String DEFLATE_HINT = "unpack.deflate.hint";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code false}.
+         */
+        static final String FALSE = "false";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code keep}.
+         */
+        static final String KEEP = "keep";//$NON-NLS-1$
+
+        /**
+         * the progress as a {@code percentage}.
+         */
+        static final String PROGRESS = "unpack.progress";//$NON-NLS-1$
+
+        /**
+         * a String representation of {@code true}.
+         */
+        static final String TRUE = "true";//$NON-NLS-1$
+
+        /**
+         * Returns a sorted map of the properties of this unpacker.
+         *
+         * @return the properties of unpacker.
+         */
+        SortedMap<String, String> properties();
+
+        /**
+         * Unpack the specified stream to the specified JAR output stream.
+         *
+         * @param in
+         *            stream to uncompressed.
+         * @param out
+         *            JAR output stream of uncompressed data.
+         * @throws IOException
+         *             if I/O exception occurs.
+         */
+        void unpack(InputStream in, JarOutputStream out) throws IOException;
+
+        /**
+         * Unpack the contents of the specified {@code File} to the specified
+         * JAR output stream.
+         *
+         * @param in
+         *            file to be uncompressed.
+         * @param out
+         *            JAR output stream of uncompressed data.
+         * @throws IOException
+         *             if I/O exception occurs.
+         */
+        void unpack(File in, JarOutputStream out) throws IOException;
+
+        /**
+         * add a listener for {@code PropertyChange} events.
+         *
+         * @param listener
+         *            the listener to listen if {@code PropertyChange} events
+         *            occurs.
+         */
+        void addPropertyChangeListener(PropertyChangeListener listener);
+
+        /**
+         * remove a listener.
+         *
+         * @param listener
+         *            listener to remove.
+         */
+        void removePropertyChangeListener(PropertyChangeListener listener);
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
new file mode 100644
index 0000000..e80a150
--- /dev/null
+++ b/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.harmony.archive.internal.nls;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+//import org.apache.harmony.kernel.vm.VM;
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ *    org.apache.harmony.archive.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ *
+ */
+public class Messages {
+
+    // ResourceBundle holding the system messages.
+    static private ResourceBundle bundle = null;
+
+    /**
+     * Retrieves a message which has no arguments.
+     *
+     * @param msg
+     *            String the key to look up.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg) {
+        if (bundle == null) {
+            return msg;
+        }
+        try {
+            return bundle.getString(msg);
+        } catch (MissingResourceException e) {
+            return "Missing message: " + msg; //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Retrieves a message which takes 1 argument.
+     *
+     * @param msg
+     *            String the key to look up.
+     * @param arg
+     *            Object the object to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, Object arg) {
+        return getString(msg, new Object[] { arg });
+    }
+
+    /**
+     * Retrieves a message which takes 1 integer argument.
+     *
+     * @param msg
+     *            String the key to look up.
+     * @param arg
+     *            int the integer to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, int arg) {
+        return getString(msg, new Object[] { Integer.toString(arg) });
+    }
+
+    /**
+     * Retrieves a message which takes 1 character argument.
+     *
+     * @param msg
+     *            String the key to look up.
+     * @param arg
+     *            char the character to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, char arg) {
+        return getString(msg, new Object[] { String.valueOf(arg) });
+    }
+
+    /**
+     * Retrieves a message which takes 2 arguments.
+     *
+     * @param msg
+     *            String the key to look up.
+     * @param arg1
+     *            Object an object to insert in the formatted output.
+     * @param arg2
+     *            Object another object to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, Object arg1, Object arg2) {
+        return getString(msg, new Object[] { arg1, arg2 });
+    }
+
+    /**
+     * Retrieves a message which takes several arguments.
+     *
+     * @param msg
+     *            String the key to look up.
+     * @param args
+     *            Object[] the objects to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, Object[] args) {
+        String format = msg;
+
+        if (bundle != null) {
+            try {
+                format = bundle.getString(msg);
+            } catch (MissingResourceException e) {
+                // ignore
+            }
+        }
+
+        return format(format, args);
+    }
+
+    /**
+     * Generates a formatted text string given a source string containing
+     * "argument markers" of the form "{argNum}" where each argNum must be in
+     * the range 0..9. The result is generated by inserting the toString of each
+     * argument into the position indicated in the string.
+     * <p>
+     * To insert the "{" character into the output, use a single backslash
+     * character to escape it (i.e. "\{"). The "}" character does not need to be
+     * escaped.
+     *
+     * @param format
+     *            String the format to use when printing.
+     * @param args
+     *            Object[] the arguments to use.
+     * @return String the formatted message.
+     */
+    public static String format(String format, Object[] args) {
+        StringBuilder answer = new StringBuilder(format.length()
+                + (args.length * 20));
+        String[] argStrings = new String[args.length];
+        for (int i = 0; i < args.length; ++i) {
+            if (args[i] == null) {
+                argStrings[i] = "<null>"; //$NON-NLS-1$
+            } else {
+                argStrings[i] = args[i].toString();
+            }
+        }
+        int lastI = 0;
+        for (int i = format.indexOf('{', 0); i >= 0; i = format.indexOf('{',
+                lastI)) {
+            if (i != 0 && format.charAt(i - 1) == '\\') {
+                // It's escaped, just print and loop.
+                if (i != 1) {
+                    answer.append(format.substring(lastI, i - 1));
+                }
+                answer.append('{');
+                lastI = i + 1;
+            } else {
+                // It's a format character.
+                if (i > format.length() - 3) {
+                    // Bad format, just print and loop.
+                    answer.append(format.substring(lastI, format.length()));
+                    lastI = format.length();
+                } else {
+                    int argnum = (byte) Character.digit(format.charAt(i + 1),
+                            10);
+                    if (argnum < 0 || format.charAt(i + 2) != '}') {
+                        // Bad format, just print and loop.
+                        answer.append(format.substring(lastI, i + 1));
+                        lastI = i + 1;
+                    } else {
+                        // Got a good one!
+                        answer.append(format.substring(lastI, i));
+                        if (argnum >= argStrings.length) {
+                            answer.append("<missing argument>"); //$NON-NLS-1$
+                        } else {
+                            answer.append(argStrings[argnum]);
+                        }
+                        lastI = i + 3;
+                    }
+                }
+            }
+        }
+        if (lastI < format.length()) {
+            answer.append(format.substring(lastI, format.length()));
+        }
+        return answer.toString();
+    }
+
+    /**
+     * Changes the locale of the messages.
+     *
+     * @param locale
+     *            Locale the locale to change to.
+     */
+    static public ResourceBundle setLocale(final Locale locale,
+            final String resource) {
+        try {
+            // VM.bootCallerClassLoader() returns null
+            final ClassLoader loader = null;//VM.bootCallerClassLoader();
+            return (ResourceBundle) AccessController
+                    .doPrivileged(new PrivilegedAction<Object>() {
+                        public Object run() {
+                            return ResourceBundle.getBundle(resource, locale,
+                                    loader != null ? loader : ClassLoader
+                                            .getSystemClassLoader());
+                        }
+                    });
+        } catch (MissingResourceException e) {
+            // ignore
+        }
+        return null;
+    }
+
+    static {
+        // Attempt to load the messages.
+        try {
+            bundle = setLocale(Locale.getDefault(),
+                    "org.apache.harmony.archive.internal.nls.messages"); //$NON-NLS-1$
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Archive.java b/src/main/java/org/apache/harmony/pack200/Archive.java
new file mode 100644
index 0000000..92c0ca4
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Archive.java
@@ -0,0 +1,332 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Archive is the main entry point to pack200 and represents a packed archive.
+ * An archive is constructed with either a JarInputStream and an output stream
+ * or a JarFile as input and an OutputStream. Options can be set, then
+ * <code>pack()</code> is called, to pack the Jar file into a pack200 archive.
+ */
+public class Archive {
+
+    private final JarInputStream jarInputStream;
+    private final OutputStream outputStream;
+    private JarFile jarFile;
+    private long currentSegmentSize;
+    private final PackingOptions options;
+
+    /**
+     * Creates an Archive with streams for the input and output.
+     *
+     * @param inputStream
+     * @param outputStream
+     * @param options - packing options (if null then defaults are used)
+     * @throws IOException
+     */
+    public Archive(JarInputStream inputStream, OutputStream outputStream,
+            PackingOptions options) throws IOException {
+        jarInputStream = inputStream;
+        if(options == null) {
+            // use all defaults
+            options = new PackingOptions();
+        }
+        this.options = options;
+        if (options.isGzip()) {
+            outputStream = new GZIPOutputStream(outputStream);
+        }
+        this.outputStream = new BufferedOutputStream(outputStream);
+        PackingUtils.config(options);
+    }
+
+    /**
+     * Creates an Archive with the given input file and a stream for the output
+     *
+     * @param jarFile - the input file
+     * @param outputStream
+     * @param options - packing options (if null then defaults are used)
+     * @throws IOException
+     */
+    public Archive(JarFile jarFile, OutputStream outputStream,
+            PackingOptions options) throws IOException {
+        if(options == null) { // use all defaults
+            options = new PackingOptions();
+        }
+        this.options = options;
+        if (options.isGzip()) {
+            outputStream = new GZIPOutputStream(outputStream);
+        }
+        this.outputStream = new BufferedOutputStream(outputStream);
+        this.jarFile = jarFile;
+        jarInputStream = null;
+        PackingUtils.config(options);
+    }
+
+    /**
+     * Pack the archive
+     * @throws Pack200Exception
+     * @throws IOException
+     */
+    public void pack() throws Pack200Exception, IOException {
+        if (0 == options.getEffort()) {
+            doZeroEffortPack();
+        } else {
+            doNormalPack();
+        }
+    }
+
+    private void doZeroEffortPack() throws IOException, Pack200Exception {
+        PackingUtils.log("Start to perform a zero-effort packing");
+        if (jarInputStream != null) {
+            PackingUtils.copyThroughJar(jarInputStream, outputStream);
+        } else {
+            PackingUtils.copyThroughJar(jarFile, outputStream);
+        }
+    }
+
+    private void doNormalPack() throws IOException, Pack200Exception {
+        PackingUtils.log("Start to perform a normal packing");
+        List packingFileList;
+        if (jarInputStream != null) {
+            packingFileList = PackingUtils.getPackingFileListFromJar(
+                    jarInputStream, options.isKeepFileOrder());
+        } else {
+            packingFileList = PackingUtils.getPackingFileListFromJar(jarFile,
+                    options.isKeepFileOrder());
+        }
+
+        List segmentUnitList = splitIntoSegments(packingFileList);
+        int previousByteAmount = 0;
+        int packedByteAmount = 0;
+
+        int segmentSize = segmentUnitList.size();
+        SegmentUnit segmentUnit;
+        for (int index = 0; index < segmentSize; index++) {
+            segmentUnit = (SegmentUnit) segmentUnitList.get(index);
+            new Segment().pack(segmentUnit, outputStream, options);
+            previousByteAmount += segmentUnit.getByteAmount();
+            packedByteAmount += segmentUnit.getPackedByteAmount();
+        }
+
+        PackingUtils.log("Total: Packed " + previousByteAmount
+                + " input bytes of " + packingFileList.size() + " files into "
+                + packedByteAmount + " bytes in " + segmentSize + " segments");
+
+        outputStream.close();
+    }
+
+    private List splitIntoSegments(List packingFileList) throws IOException,
+            Pack200Exception {
+        List segmentUnitList = new ArrayList();
+        List classes = new ArrayList();
+        List files = new ArrayList();
+        long segmentLimit = options.getSegmentLimit();
+
+        int size = packingFileList.size();
+        PackingFile packingFile;
+        for (int index = 0; index < size; index++) {
+            packingFile = (PackingFile) packingFileList.get(index);
+            if (!addJarEntry(packingFile, classes, files)) {
+                // not added because segment has reached maximum size
+                segmentUnitList.add(new SegmentUnit(classes, files));
+                classes = new ArrayList();
+                files = new ArrayList();
+                currentSegmentSize = 0;
+                // add the jar to a new segment
+                addJarEntry(packingFile, classes, files);
+                // ignore the size of first entry for compatibility with RI
+                currentSegmentSize = 0;
+            } else if (segmentLimit == 0 && estimateSize(packingFile) > 0) {
+                // create a new segment for each class unless size is 0
+                segmentUnitList.add(new SegmentUnit(classes, files));
+                classes = new ArrayList();
+                files = new ArrayList();
+            }
+        }
+        if (classes.size() > 0 && files.size() > 0) {
+            segmentUnitList.add(new SegmentUnit(classes, files));
+        }
+        return segmentUnitList;
+    }
+
+    private boolean addJarEntry(PackingFile packingFile, List javaClasses,
+            List files) throws IOException, Pack200Exception {
+        long segmentLimit = options.getSegmentLimit();
+        if (segmentLimit != -1 && segmentLimit != 0) {
+            // -1 is a special case where only one segment is created and
+            // 0 is a special case where one segment is created for each file
+            // except for files in "META-INF"
+            long packedSize = estimateSize(packingFile);
+            if (packedSize + currentSegmentSize > segmentLimit
+                    && currentSegmentSize > 0) {
+                // don't add this JarEntry to the current segment
+                return false;
+            } else {
+                // do add this JarEntry
+                currentSegmentSize += packedSize;
+            }
+        }
+
+        String name = packingFile.getName();
+        if (name.endsWith(".class") && !options.isPassFile(name)) {
+            Pack200ClassReader classParser = new Pack200ClassReader(
+                    packingFile.contents);
+            classParser.setFileName(name);
+            javaClasses.add(classParser);
+            packingFile.contents = new byte[0];
+        }
+        files.add(packingFile);
+        return true;
+    }
+
+    private long estimateSize(PackingFile packingFile) {
+        // The heuristic used here is for compatibility with the RI and should
+        // not be changed
+        String name = packingFile.getName();
+        if (name.startsWith("META-INF") || name.startsWith("/META-INF")) {
+            return 0;
+        } else {
+            long fileSize = packingFile.contents.length;
+            if (fileSize < 0) {
+                fileSize = 0;
+            }
+            return name.length() + fileSize + 5;
+        }
+    }
+
+    static class SegmentUnit {
+
+        private final List classList;
+
+        private final List fileList;
+
+        private int byteAmount = 0;
+
+        private int packedByteAmount = 0;
+
+        public SegmentUnit(List classes, List files) {
+            classList = classes;
+            fileList = files;
+
+            // Calculate the amount of bytes in classes and files before packing
+            Pack200ClassReader classReader;
+            for (Iterator iterator = classList.iterator(); iterator.hasNext();) {
+                classReader = (Pack200ClassReader) iterator.next();
+                byteAmount += classReader.b.length;
+            }
+
+            PackingFile file;
+            for (Iterator iterator = fileList.iterator(); iterator.hasNext();) {
+                file = (PackingFile) iterator.next();
+                byteAmount += file.contents.length;
+            }
+        }
+
+        public List getClassList() {
+            return classList;
+        }
+
+        public int classListSize() {
+            return classList.size();
+        }
+
+        public int fileListSize() {
+            return fileList.size();
+        }
+
+        public List getFileList() {
+            return fileList;
+        }
+
+        public int getByteAmount() {
+            return byteAmount;
+        }
+
+        public int getPackedByteAmount() {
+            return packedByteAmount;
+        }
+
+        public void addPackedByteAmount(int amount) {
+            packedByteAmount += amount;
+        }
+    }
+
+    static class PackingFile {
+
+        private final String name;
+        private byte[] contents;
+        private final long modtime;
+        private final boolean deflateHint;
+        private final boolean isDirectory;
+
+        public PackingFile(String name, byte[] contents, long modtime) {
+            this.name = name;
+            this.contents = contents;
+            this.modtime = modtime;
+            deflateHint = false;
+            isDirectory = false;
+        }
+
+        public PackingFile(byte[] bytes, JarEntry jarEntry) {
+            name = jarEntry.getName();
+            contents = bytes;
+            modtime = jarEntry.getTime();
+            deflateHint = jarEntry.getMethod() == JarEntry.DEFLATED;
+            isDirectory = jarEntry.isDirectory();
+        }
+
+        public byte[] getContents() {
+            return contents;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public long getModtime() {
+            return modtime;
+        }
+
+        public void setContents(byte[] contents) {
+            this.contents = contents;
+        }
+
+        public boolean isDefalteHint() {
+            return deflateHint;
+        }
+
+        public boolean isDirectory(){
+            return isDirectory;
+        }
+
+        public String toString() {
+            return name;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java b/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java
new file mode 100644
index 0000000..5afb5ba
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java
@@ -0,0 +1,260 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Attribute;
+
+/**
+ * Attribute Definition bands define how any unknown attributes should be read
+ * by the decompressor.
+ */
+public class AttributeDefinitionBands extends BandSet {
+
+    public static final int CONTEXT_CLASS = 0;
+    public static final int CONTEXT_CODE = 3;
+    public static final int CONTEXT_FIELD = 1;
+    public static final int CONTEXT_METHOD = 2;
+
+    private final List classAttributeLayouts = new ArrayList();
+    private final List methodAttributeLayouts = new ArrayList();
+    private final List fieldAttributeLayouts = new ArrayList();
+    private final List codeAttributeLayouts = new ArrayList();
+
+    private final List attributeDefinitions = new ArrayList();
+
+    private final CpBands cpBands;
+    private final Segment segment;
+
+    public AttributeDefinitionBands(Segment segment, int effort,
+            Attribute[] attributePrototypes) {
+        super(effort, segment.getSegmentHeader());
+        this.cpBands = segment.getCpBands();
+        this.segment = segment;
+        Map classLayouts = new HashMap();
+        Map methodLayouts = new HashMap();
+        Map fieldLayouts = new HashMap();
+        Map codeLayouts = new HashMap();
+
+        for (int i = 0; i < attributePrototypes.length; i++) {
+            NewAttribute newAttribute = (NewAttribute) attributePrototypes[i];
+            if(!(newAttribute instanceof NewAttribute.ErrorAttribute) && !(newAttribute instanceof NewAttribute.PassAttribute) && !(newAttribute instanceof NewAttribute.StripAttribute)) {
+                if (newAttribute.isContextClass()) {
+                    classLayouts.put(newAttribute.type, newAttribute.getLayout());
+                }
+                if (newAttribute.isContextMethod()) {
+                    methodLayouts.put(newAttribute.type, newAttribute.getLayout());
+                }
+                if (newAttribute.isContextField()) {
+                    fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
+                }
+                if (newAttribute.isContextCode()) {
+                    codeLayouts.put(newAttribute.type, newAttribute.getLayout());
+                }
+            }
+        }
+        if (classLayouts.keySet().size() > 7) {
+            segmentHeader.setHave_class_flags_hi(true);
+        }
+        if (methodLayouts.keySet().size() > 6) {
+            segmentHeader.setHave_method_flags_hi(true);
+        }
+        if (fieldLayouts.keySet().size() > 10) {
+            segmentHeader.setHave_field_flags_hi(true);
+        }
+        if (codeLayouts.keySet().size() > 15) {
+            segmentHeader.setHave_code_flags_hi(true);
+        }
+        int[] availableClassIndices = new int[] { 25, 26, 27, 28, 29, 30, 31 };
+        if (classLayouts.size() > 7) {
+            availableClassIndices = addHighIndices(availableClassIndices);
+        }
+        addAttributeDefinitions(classLayouts, availableClassIndices,
+                CONTEXT_CLASS);
+        int[] availableMethodIndices = new int[] { 26, 27, 28, 29, 30, 31 };
+        if (methodAttributeLayouts.size() > 6) {
+            availableMethodIndices = addHighIndices(availableMethodIndices);
+        }
+        addAttributeDefinitions(methodLayouts, availableMethodIndices,
+                CONTEXT_METHOD);
+        int[] availableFieldIndices = new int[] { 18, 23, 24, 25, 26, 27, 28,
+                29, 30, 31 };
+        if (fieldAttributeLayouts.size() > 10) {
+            availableFieldIndices = addHighIndices(availableFieldIndices);
+        }
+        addAttributeDefinitions(fieldLayouts, availableFieldIndices,
+                CONTEXT_FIELD);
+        int[] availableCodeIndices = new int[] { 17, 18, 19, 20, 21, 22, 23,
+                24, 25, 26, 27, 28, 29, 30, 31 };
+        if (codeAttributeLayouts.size() > 15) {
+            availableCodeIndices = addHighIndices(availableCodeIndices);
+        }
+        addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
+    }
+
+    /**
+     * All input classes for the segment have now been read in, so this method
+     * is called so that this class can calculate/complete anything it could not
+     * do while classes were being read.
+     */
+    public void finaliseBands() {
+        addSyntheticDefinitions();
+        segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing attribute definition bands...");
+        int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
+        int[] attributeDefinitionName = new int[attributeDefinitions.size()];
+        int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
+        for (int i = 0; i < attributeDefinitionLayout.length; i++) {
+            AttributeDefinition def = (AttributeDefinition) attributeDefinitions
+                    .get(i);
+            attributeDefinitionHeader[i] = def.contextType
+                    | (def.index + 1 << 2);
+            attributeDefinitionName[i] = def.name.getIndex();
+            attributeDefinitionLayout[i] = def.layout.getIndex();
+        }
+
+        byte[] encodedBand = encodeBandInt("attributeDefinitionHeader",
+                attributeDefinitionHeader, Codec.BYTE1);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from attributeDefinitionHeader["
+                + attributeDefinitionHeader.length + "]");
+
+        encodedBand = encodeBandInt("attributeDefinitionName",
+                attributeDefinitionName, Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from attributeDefinitionName["
+                + attributeDefinitionName.length + "]");
+
+        encodedBand = encodeBandInt("attributeDefinitionLayout",
+                attributeDefinitionLayout, Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from attributeDefinitionLayout["
+                + attributeDefinitionLayout.length + "]");
+    }
+
+    private void addSyntheticDefinitions() {
+        boolean anySytheticClasses = segment.getClassBands()
+                .isAnySyntheticClasses();
+        boolean anySyntheticMethods = segment.getClassBands()
+                .isAnySyntheticMethods();
+        boolean anySyntheticFields = segment.getClassBands()
+                .isAnySyntheticFields();
+        if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
+            CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
+            CPUTF8 emptyUTF = cpBands.getCPUtf8("");
+            if (anySytheticClasses) {
+                attributeDefinitions.add(new AttributeDefinition(12,
+                        CONTEXT_CLASS, syntheticUTF, emptyUTF));
+            }
+            if (anySyntheticMethods) {
+                attributeDefinitions.add(new AttributeDefinition(12,
+                        CONTEXT_METHOD, syntheticUTF, emptyUTF));
+            }
+            if (anySyntheticFields) {
+                attributeDefinitions.add(new AttributeDefinition(12,
+                        CONTEXT_FIELD, syntheticUTF, emptyUTF));
+            }
+        }
+    }
+
+    private int[] addHighIndices(int[] availableIndices) {
+        int[] temp = new int[availableIndices.length + 32];
+        for (int i = 0; i < availableIndices.length; i++) {
+            temp[i] = availableIndices[i];
+        }
+        int j = 32;
+        for (int i = availableIndices.length; i < temp.length; i++) {
+            temp[i] = j;
+            j++;
+        }
+        return temp;
+    }
+
+    private void addAttributeDefinitions(Map layouts, int[] availableIndices,
+            int contextType) {
+        int i = 0;
+        for (Iterator iterator = layouts.keySet().iterator(); iterator
+                .hasNext();) {
+            String name = (String) iterator.next();
+            String layout = (String) layouts.get(name);
+            int index = availableIndices[i];
+            AttributeDefinition definition = new AttributeDefinition(index,
+                    contextType, cpBands.getCPUtf8(name), cpBands
+                            .getCPUtf8(layout));
+            attributeDefinitions.add(definition);
+            switch (contextType) {
+            case CONTEXT_CLASS:
+                classAttributeLayouts.add(definition);
+                break;
+            case CONTEXT_METHOD:
+                methodAttributeLayouts.add(definition);
+                break;
+            case CONTEXT_FIELD:
+                fieldAttributeLayouts.add(definition);
+                break;
+            case CONTEXT_CODE:
+                codeAttributeLayouts.add(definition);
+            }
+        }
+    }
+
+    public List getClassAttributeLayouts() {
+        return classAttributeLayouts;
+    }
+
+    public List getMethodAttributeLayouts() {
+        return methodAttributeLayouts;
+    }
+
+    public List getFieldAttributeLayouts() {
+        return fieldAttributeLayouts;
+    }
+
+    public List getCodeAttributeLayouts() {
+        return codeAttributeLayouts;
+    }
+
+    public static class AttributeDefinition {
+
+        public int index;
+        public int contextType;
+        public CPUTF8 name;
+        public CPUTF8 layout;
+
+        public AttributeDefinition(int index, int contextType, CPUTF8 name,
+                CPUTF8 layout) {
+            this.index = index;
+            this.contextType = contextType;
+            this.name = name;
+            this.layout = layout;
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/BHSDCodec.java b/src/main/java/org/apache/harmony/pack200/BHSDCodec.java
new file mode 100644
index 0000000..8836098
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/BHSDCodec.java
@@ -0,0 +1,516 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A BHSD codec is a means of encoding integer values as a sequence of bytes or
+ * vice versa using a specified "BHSD" encoding mechanism. It uses a
+ * variable-length encoding and a modified sign representation such that small
+ * numbers are represented as a single byte, whilst larger numbers take more
+ * bytes to encode. The number may be signed or unsigned; if it is unsigned, it
+ * can be weighted towards positive numbers or equally distributed using a one's
+ * complement. The Codec also supports delta coding, where a sequence of numbers
+ * is represented as a series of first-order differences. So a delta encoding of
+ * the integers [1..10] would be represented as a sequence of 10x1s. This allows
+ * the absolute value of a coded integer to fall outside of the 'small number'
+ * range, whilst still being encoded as a single byte.
+ *
+ * A BHSD codec is configured with four parameters:
+ * <dl>
+ * <dt>B</dt>
+ * <dd>The maximum number of bytes that each value is encoded as. B must be a
+ * value between [1..5]. For a pass-through coding (where each byte is encoded
+ * as itself, aka {@link #BYTE1}, B is 1 (each byte takes a maximum of 1 byte).</dd>
+ * <dt>H</dt>
+ * <dd>The radix of the integer. Values are defined as a sequence of values,
+ * where value <code>n</code> is multiplied by <code>H^<sup>n</sup></code>.
+ * So the number 1234 may be represented as the sequence 4 3 2 1 with a radix
+ * (H) of 10. Note that other permutations are also possible; 43 2 1 will also
+ * encode 1234. The co-parameter L is defined as 256-H. This is important
+ * because only the last value in a sequence may be &lt; L; all prior values
+ * must be &gt; L.</dd>
+ * <dt>S</dt>
+ * <dd>Whether the codec represents signed values (or not). This may have 3
+ * values; 0 (unsigned), 1 (signed, one's complement) or 2 (signed, two's
+ * complement)</dd>
+ * <dt>D</dt>
+ * <dd>Whether the codec represents a delta encoding. This may be 0 (no delta)
+ * or 1 (delta encoding). A delta encoding of 1 indicates that values are
+ * cumulative; a sequence of <code>1 1 1 1 1</code> will represent the
+ * sequence <code>1 2 3 4 5</code>. For this reason, the codec supports two
+ * variants of decode; one {@link #decode(InputStream, long) with} and one
+ * {@link #decode(InputStream) without} a <code>last</code> parameter. If the
+ * codec is a non-delta encoding, then the value is ignored if passed. If the
+ * codec is a delta encoding, it is a run-time error to call the value without
+ * the extra parameter, and the previous value should be returned. (It was
+ * designed this way to support multi-threaded access without requiring a new
+ * instance of the Codec to be cloned for each use.)
+ * <dt>
+ * </dl>
+ *
+ * Codecs are notated as (B,H,S,D) and either D or S,D may be omitted if zero.
+ * Thus {@link #BYTE1} is denoted (1,256,0,0) or (1,256). The
+ * {@link #toString()} method prints out the condensed form of the encoding.
+ * Often, the last character in the name ({@link #BYTE1}, {@link #UNSIGNED5})
+ * gives a clue as to the B value. Those that start with U ({@link #UDELTA5},
+ * {@link #UNSIGNED5}) are unsigned; otherwise, in most cases, they are signed.
+ * The presence of the word Delta ({@link #DELTA5}, {@link #UDELTA5})
+ * indicates a delta encoding is used.
+ *
+ */
+public final class BHSDCodec extends Codec {
+
+    /**
+     * The maximum number of bytes in each coding word
+     */
+    private final int b;
+
+    /**
+     * Whether delta encoding is used (0=false,1=true)
+     */
+    private final int d;
+
+    /**
+     * The radix of the encoding
+     */
+    private final int h;
+
+    /**
+     * The co-parameter of h; 256-h
+     */
+    private final int l;
+
+    /**
+     * Represents signed numbers or not (0=unsigned,1/2=signed)
+     */
+    private final int s;
+
+    private long cardinality;
+
+    private final long smallest;
+
+    private final long largest;
+
+    /**
+     * radix^i powers
+     */
+    private final long[] powers;
+
+    /**
+     * Constructs an unsigned, non-delta Codec with the given B and H values.
+     *
+     * @param b
+     *            the maximum number of bytes that a value can be encoded as
+     *            [1..5]
+     * @param h
+     *            the radix of the encoding [1..256]
+     */
+    public BHSDCodec(int b, int h) {
+        this(b, h, 0, 0);
+    }
+
+    /**
+     * Constructs a non-delta Codec with the given B, H and S values.
+     *
+     * @param b
+     *            the maximum number of bytes that a value can be encoded as
+     *            [1..5]
+     * @param h
+     *            the radix of the encoding [1..256]
+     * @param s
+     *            whether the encoding represents signed numbers (s=0 is
+     *            unsigned; s=1 is signed with 1s complement; s=2 is signed with ?)
+     */
+    public BHSDCodec(int b, int h, int s) {
+        this(b, h, s, 0);
+    }
+
+    /**
+     * Constructs a Codec with the given B, H, S and D values.
+     *
+     * @param b
+     *            the maximum number of bytes that a value can be encoded as
+     *            [1..5]
+     * @param h
+     *            the radix of the encoding [1..256]
+     * @param s
+     *            whether the encoding represents signed numbers (s=0 is
+     *            unsigned; s=1 is signed with 1s complement; s=2 is signed with ?)
+     * @param d
+     *            whether this is a delta encoding (d=0 is non-delta; d=1 is
+     *            delta)
+     */
+    public BHSDCodec(int b, int h, int s, int d) {
+        if (b < 1 || b > 5) {
+            throw new IllegalArgumentException("1<=b<=5");
+        }
+        if (h < 1 || h > 256) {
+            throw new IllegalArgumentException("1<=h<=256");
+        }
+        if (s < 0 || s > 2) {
+            throw new IllegalArgumentException("0<=s<=2");
+        }
+        if (d < 0 || d > 1) {
+            throw new IllegalArgumentException("0<=d<=1");
+        }
+        if (b == 1 && h != 256) {
+            throw new IllegalArgumentException("b=1 -> h=256");
+        }
+        if (h == 256 && b == 5) {
+            throw new IllegalArgumentException("h=256 -> b!=5");
+        }
+        this.b = b;
+        this.h = h;
+        this.s = s;
+        this.d = d;
+        this.l = 256 - h;
+        if (h == 1) {
+            cardinality = b * 255 + 1;
+        } else {
+            cardinality = (long) ((long) (l * (1 - Math.pow(h, b)) / (1 - h)) + Math
+                    .pow(h, b));
+        }
+        smallest = calculateSmallest();
+        largest = calculateLargest();
+
+        powers = new long[b];
+        for(int c = 0; c < b; c++) {
+            powers[c] = (long)Math.pow(h, c);
+        }
+    }
+
+    /**
+     * Returns the cardinality of this codec; that is, the number of distinct
+     * values that it can contain.
+     *
+     * @return the cardinality of this codec
+     */
+    public long cardinality() {
+        return cardinality;
+    }
+
+    public int decode(InputStream in) throws IOException, Pack200Exception {
+        if (d != 0) {
+            throw new Pack200Exception(
+                    "Delta encoding used without passing in last value; this is a coding error");
+        }
+        return decode(in, 0);
+    }
+
+    public int decode(InputStream in, long last) throws IOException,
+            Pack200Exception {
+        int n = 0;
+        long z = 0;
+        long x = 0;
+
+        do {
+            x = in.read();
+            lastBandLength ++;
+            z += x * powers[n];
+            n++;
+        } while (x >= l && n < b);
+
+        if (x == -1) {
+            throw new EOFException("End of stream reached whilst decoding");
+        }
+        
+        if (isSigned()) {
+            int u = ((1 << s) - 1);
+            if ((z & u) == u) {
+                z = z >>> s ^ -1L;
+            } else {
+                z = z - (z >>> s);
+            }
+        }
+        // This algorithm does the same thing, but is probably slower. Leaving
+        // in for now for readability
+        // if(isSigned()) {
+        // long u = z;
+        // long twoPowS = (long)Math.pow(2, s);
+        // double twoPowSMinusOne = twoPowS-1;
+        // if(u % twoPowS < twoPowSMinusOne) {
+        // if(cardinality < Math.pow(2, 32)) {
+        // z = (long) (u - (Math.floor(u/ twoPowS)));
+        // } else {
+        // z = cast32((long) (u - (Math.floor(u/ twoPowS))));
+        // }
+        // } else {
+        // z = (long) (-Math.floor(u/ twoPowS) - 1);
+        // }
+        // }
+        if (isDelta()) {
+            z += last;
+        }
+        return (int)z;
+    }
+
+    public int[] decodeInts(int n, InputStream in) throws IOException,
+            Pack200Exception {
+        int[] band = super.decodeInts(n, in);
+        if (isDelta()) {
+            for (int i = 0; i < band.length; i++) {
+                while (band[i] > largest) {
+                    band[i] -= cardinality;
+                }
+                while (band[i] < smallest) {
+                    band[i] += cardinality;
+                }
+            }
+        }
+        return band;
+    }
+
+    public int[] decodeInts(int n, InputStream in, int firstValue)
+            throws IOException, Pack200Exception {
+        int[] band =  super.decodeInts(n, in, firstValue);
+        if (isDelta()) {
+            for (int i = 0; i < band.length; i++) {
+                while (band[i] > largest) {
+                    band[i] -= cardinality;
+                }
+                while (band[i] < smallest) {
+                    band[i] += cardinality;
+                }
+            }
+        }
+        return band;
+    }
+
+    // private long cast32(long u) {
+    // u = (long) ((long) ((u + Math.pow(2, 31)) % Math.pow(2, 32)) -
+    // Math.pow(2, 31));
+    // return u;
+    // }
+
+    /**
+     * True if this encoding can code the given value
+     *
+     * @param value
+     *            the value to check
+     * @return <code>true</code> if the encoding can encode this value
+     */
+    public boolean encodes(long value) {
+        return value >= smallest && value <= largest;
+    }
+
+    public byte[] encode(int value, int last) throws Pack200Exception {
+        if(!encodes(value)) {
+            throw new Pack200Exception("The codec " + toString()
+                  + " does not encode the value " + value);
+        }
+
+        long z = value;
+        if (isDelta()) {
+            z -= last;
+        }
+        if (isSigned()) {
+            if(z < Integer.MIN_VALUE) {
+                z += 4294967296L;
+            } else if (z > Integer.MAX_VALUE) {
+                z -= 4294967296L;
+            }
+            if (z < 0) {
+                z = (-z << s) - 1;
+            } else {
+                if (s == 1) {
+                    z = z << s;
+                } else {
+                    z += (z - z % 3) / 3;
+                }
+            }
+        } else {
+            if (z < 0) {
+                // Need to use integer overflow here to represent negatives.
+                if (cardinality < 4294967296L) {
+                    z += cardinality;
+                } else {
+                    z += 4294967296L; // this value is equal to (1 << 32).
+                }
+            }
+        }
+        if (z < 0) {
+            throw new Pack200Exception("unable to encode");
+        }
+
+        List byteList = new ArrayList();
+        for (int n = 0; n < b; n++) {
+            long byteN;
+            if (z < l) {
+                byteN = z;
+            } else {
+                byteN = z % h;
+                while (byteN < l) {
+                    byteN += h;
+                }
+            }
+            byteList.add(new Byte((byte) byteN));
+            if (byteN < l) {
+                break;
+            }
+            z -= byteN;
+            z /= h;
+        }
+        byte[] bytes = new byte[byteList.size()];
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] = ((Byte) byteList.get(i)).byteValue();
+        }
+        return bytes;
+    }
+
+    public byte[] encode(int value) throws Pack200Exception {
+        return encode(value, 0);
+    }
+
+    /**
+     * Returns true if this codec is a delta codec
+     *
+     * @return true if this codec is a delta codec
+     */
+    public boolean isDelta() {
+        return d != 0;
+    }
+
+    /**
+     * Returns true if this codec is a signed codec
+     *
+     * @return true if this codec is a signed codec
+     */
+    public boolean isSigned() {
+        return s != 0;
+    }
+
+    /**
+     * Returns the largest value that this codec can represent.
+     *
+     * @return the largest value that this codec can represent.
+     */
+    public long largest() {
+        return largest;
+    }
+
+    private long calculateLargest() {
+        long result;
+        // TODO This can probably be optimized into a better mathematical
+        // statement
+        if (d == 1) {
+            BHSDCodec bh0 = new BHSDCodec(b, h);
+            return bh0.largest();
+        } else if (s == 0) {
+            result = cardinality() - 1;
+        } else if (s == 1) {
+            result = cardinality() / 2 - 1;
+        } else if (s == 2) {
+            result = (3L * cardinality()) / 4 - 1;
+        } else {
+            throw new Error("Unknown s value");
+        }
+        return Math.min((s == 0 ? ((long) Integer.MAX_VALUE) << 1
+                : Integer.MAX_VALUE) - 1, result);
+    }
+
+    /**
+     * Returns the smallest value that this codec can represent.
+     *
+     * @return the smallest value that this codec can represent.
+     */
+    public long smallest() {
+        return smallest;
+    }
+
+    private long calculateSmallest() {
+        long result;
+        if (d == 1 || !isSigned()) {
+            if (cardinality >= 4294967296L) { // 2^32
+                result = Integer.MIN_VALUE;
+            } else {
+                result = 0;
+            }
+        } else {
+            result = Math.max(Integer.MIN_VALUE, -cardinality() / (1 << s));
+        }
+        return result;
+    }
+
+    /**
+     * Returns the codec in the form (1,256) or (1,64,1,1). Note that trailing
+     * zero fields are not shown.
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer(11);
+        buffer.append('(');
+        buffer.append(b);
+        buffer.append(',');
+        buffer.append(h);
+        if (s != 0 || d != 0) {
+            buffer.append(',');
+            buffer.append(s);
+        }
+        if (d != 0) {
+            buffer.append(',');
+            buffer.append(d);
+        }
+        buffer.append(')');
+        return buffer.toString();
+    }
+
+    /**
+     * @return the b
+     */
+    public int getB() {
+        return b;
+    }
+
+    /**
+     * @return the h
+     */
+    public int getH() {
+        return h;
+    }
+
+    /**
+     * @return the s
+     */
+    public int getS() {
+        return s;
+    }
+
+    /**
+     * @return the l
+     */
+    public int getL() {
+        return l;
+    }
+
+    public boolean equals(Object o) {
+        if (o instanceof BHSDCodec) {
+            BHSDCodec codec = (BHSDCodec) o;
+            return codec.b == b && codec.h == h && codec.s == s && codec.d == d;
+        }
+        return false;
+    }
+
+    public int hashCode() {
+        return ((b* 37 + h) * 37 + s) * 37 + d;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/BandSet.java b/src/main/java/org/apache/harmony/pack200/BandSet.java
new file mode 100644
index 0000000..c4f573a
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/BandSet.java
@@ -0,0 +1,788 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract superclass for a set of bands
+ */
+public abstract class BandSet {
+
+    protected final SegmentHeader segmentHeader;
+    final int effort;
+
+    // Minimum size of band for each effort level where we consider alternative codecs
+    // Note: these values have been tuned - please test carefully if changing them
+    private static final int[] effortThresholds = new int[] {0, 0, 1000, 500, 100, 100, 100, 100, 100, 0};
+
+    private long[] canonicalLargest;
+    private long[] canonicalSmallest;
+
+    /**
+     * Create a new BandSet
+     * @param effort - the packing effort to be used (must be 1-9)
+     * @param header - the segment header
+     */
+    public BandSet(int effort, SegmentHeader header) {
+        this.effort = effort;
+        this.segmentHeader = header;
+    }
+
+    /**
+     * Write the packed set of bands to the given output stream
+     * @param out
+     * @throws IOException
+     * @throws Pack200Exception
+     */
+    public abstract void pack(OutputStream out) throws IOException, Pack200Exception;
+
+    /**
+     * Encode a band without considering other Codecs
+     * @param band - the band
+     * @param codec - the Codec to use
+     * @return the encoded band
+     * @throws Pack200Exception
+     */
+    public byte[] encodeScalar(int[] band, BHSDCodec codec) throws Pack200Exception {
+        return codec.encode(band);
+    }
+
+    /**
+     * Encode a single value with the given Codec
+     * @param value - the value to encode
+     * @param codec - Codec to use
+     * @return the encoded value
+     * @throws Pack200Exception
+     */
+    public byte[] encodeScalar(int value, BHSDCodec codec) throws Pack200Exception {
+        return codec.encode(value);
+    }
+
+    /**
+     * Encode a band of integers. The default codec may be used, but other
+     * Codecs are considered if effort is greater than 1.
+     *
+     * @param name
+     *            - name of the band (used for debugging)
+     * @param ints
+     *            - the band
+     * @param defaultCodec
+     *            - the default Codec
+     * @return the encoded band
+     * @throws Pack200Exception
+     */
+    public byte[] encodeBandInt(String name, int[] ints, BHSDCodec defaultCodec) throws Pack200Exception {
+        byte[] encodedBand = null;
+     // Useful for debugging
+//        if(ints.length > 0) {
+//            System.out.println("encoding " + name + " " + ints.length);
+//        }
+        if(effort > 1 && (ints.length >= effortThresholds[effort])) {
+            BandAnalysisResults results = analyseBand(name, ints, defaultCodec);
+            Codec betterCodec = results.betterCodec;
+            encodedBand = results.encodedBand;
+            if(betterCodec != null) {
+                if(betterCodec instanceof BHSDCodec) {
+                    int[] specifierBand = CodecEncoding.getSpecifier(betterCodec, defaultCodec);
+                    int specifier = specifierBand[0];
+                    if(specifierBand.length > 1) {
+                        for (int i = 1; i < specifierBand.length; i++) {
+                            segmentHeader.appendBandCodingSpecifier(specifierBand[i]);
+                        }
+                    }
+                    if(defaultCodec.isSigned()) {
+                        specifier = -1 -specifier;
+                    } else {
+                        specifier = specifier + defaultCodec.getL();
+                    }
+                    byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+                    byte[] band = new byte[specifierEncoded.length + encodedBand.length];
+                    System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
+                    System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
+                    return band;
+                } else if (betterCodec instanceof PopulationCodec) {
+                    int[] extraSpecifierInfo = results.extraMetadata;
+                    for (int i = 0; i < extraSpecifierInfo.length; i++) {
+                        segmentHeader.appendBandCodingSpecifier(extraSpecifierInfo[i]);
+                    }
+                    return encodedBand;
+                } else if (betterCodec instanceof RunCodec) {
+
+                }
+            }
+        }
+
+        // If we get here then we've decided to use the default codec.
+        if(ints.length > 0) {
+            if(encodedBand == null) {
+                encodedBand = defaultCodec.encode(ints);
+            }
+            int first = ints[0];
+            if(defaultCodec.getB() != 1) {
+                if (defaultCodec.isSigned() && first >= -256 && first <= -1) {
+                    int specifier = -1 - CodecEncoding.getSpecifierForDefaultCodec(defaultCodec);
+                    byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+                    byte[] band = new byte[specifierEncoded.length + encodedBand.length];
+                    System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
+                    System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
+                    return band;
+                } else if (!defaultCodec.isSigned() && first >= defaultCodec.getL()
+                        && first <= defaultCodec.getL() + 255) {
+                    int specifier = CodecEncoding.getSpecifierForDefaultCodec(defaultCodec) + defaultCodec.getL();
+                    byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+                    byte[] band = new byte[specifierEncoded.length + encodedBand.length];
+                    System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
+                    System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
+                    return band;
+                }
+            }
+            return encodedBand;
+        }
+        return new byte[0];
+    }
+
+    private BandAnalysisResults analyseBand(String name, int[] band,
+            BHSDCodec defaultCodec) throws Pack200Exception {
+
+        BandAnalysisResults results = new BandAnalysisResults();
+
+        if(canonicalLargest == null) {
+            canonicalLargest = new long[116];
+            canonicalSmallest = new long[116];
+            for (int i = 1; i < canonicalLargest.length; i++) {
+                canonicalLargest[i] = CodecEncoding.getCanonicalCodec(i).largest();
+                canonicalSmallest[i] = CodecEncoding.getCanonicalCodec(i).smallest();
+            }
+        }
+        BandData bandData = new BandData(band);
+
+        // Check that there is a reasonable saving to be made
+        byte[] encoded = defaultCodec.encode(band);
+        results.encodedBand = encoded;
+
+        // Note: these values have been tuned - please test carefully if changing them
+        if(encoded.length <= band.length + 23 - 2*effort) { // TODO: tweak
+            return results;
+        }
+
+        // Check if we can use BYTE1 as that's a 1:1 mapping if we can
+        if(!bandData.anyNegatives() && bandData.largest <= Codec.BYTE1.largest()) {
+              results.encodedBand = Codec.BYTE1.encode(band)  ;
+              results.betterCodec = Codec.BYTE1;
+              return results;
+        }
+
+        // Consider a population codec (but can't be nested)
+        if(effort > 3 && !name.equals("POPULATION")) {
+            int numDistinctValues = bandData.numDistinctValues();
+            float distinctValuesAsProportion = (float)numDistinctValues / (float)band.length;
+
+            // Note: these values have been tuned - please test carefully if changing them
+            if(numDistinctValues < 100 || distinctValuesAsProportion < 0.02 ||  (effort > 6 && distinctValuesAsProportion < 0.04)) { // TODO: tweak
+                encodeWithPopulationCodec(name, band, defaultCodec, bandData, results);
+                if(timeToStop(results)) {
+                    return results;
+                }
+            }
+        }
+
+        List codecFamiliesToTry = new ArrayList();
+
+        // See if the deltas are mainly small increments
+        if(bandData.mainlyPositiveDeltas() && bandData.mainlySmallDeltas()) {
+            codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs2);
+        }
+
+        if (bandData.wellCorrelated()) { // Try delta encodings
+            if (bandData.mainlyPositiveDeltas()) {
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs3);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs4);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs5);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs3);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs4);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs5);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs2);
+            } else {
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs3);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs2);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs4);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs5);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs2);
+            }
+        } else {
+            if (bandData.anyNegatives()) {
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaSignedCodecs2);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs2);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs3);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs4);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaSignedCodecs5);
+            } else {
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs3);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs4);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs5);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.nonDeltaUnsignedCodecs2);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs1);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs3);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs4);
+                codecFamiliesToTry.add(CanonicalCodecFamilies.deltaUnsignedCodecs5);
+            }
+        }
+        if(name.equalsIgnoreCase("cpint")) {
+            System.out.print("");
+        }
+
+        for (Iterator iterator = codecFamiliesToTry.iterator(); iterator
+                .hasNext();) {
+            BHSDCodec[] family = (BHSDCodec[]) iterator.next();
+            tryCodecs(name, band, defaultCodec, bandData, results, encoded,
+                    family);
+            if (timeToStop(results)) {
+                break;
+            }
+        }
+
+        return results;
+    }
+
+    private boolean timeToStop(BandAnalysisResults results) {
+        // if tried more than effort number of codecs for this band then return
+        // Note: these values have been tuned - please test carefully if changing them
+        if(effort > 6) {
+            return results.numCodecsTried >= effort * 2;
+        }
+        return results.numCodecsTried >= effort;
+        // May want to also check how much we've saved if performance needs improving, e.g. saved more than effort*2 %
+        // || (float)results.saved/(float)results.encodedBand.length > (float)effort * 2/100;
+    }
+
+    private void tryCodecs(String name, int[] band, BHSDCodec defaultCodec, BandData bandData,
+            BandAnalysisResults results, byte[] encoded,
+            BHSDCodec[] potentialCodecs) throws Pack200Exception {
+        for (int i = 0; i < potentialCodecs.length; i++) {
+            BHSDCodec potential = potentialCodecs[i];
+            if(potential.equals(defaultCodec)) {
+                return; // don't try codecs with greater cardinality in the same 'family' as the default codec as there won't be any savings
+            }
+            if (potential.isDelta()) {
+                if (potential.largest() >= bandData.largestDelta
+                        && potential.smallest() <= bandData.smallestDelta
+                        && potential.largest() >= bandData.largest
+                        && potential.smallest() <= bandData.smallest) {
+                    // TODO: can represent some negative deltas with overflow
+                    byte[] encoded2 = potential.encode(band);
+                    results.numCodecsTried++;
+                    byte[] specifierEncoded = defaultCodec.encode(CodecEncoding
+                            .getSpecifier(potential, null));
+                    int saved = encoded.length - encoded2.length
+                            - specifierEncoded.length;
+                    if (saved > results.saved) {
+                        results.betterCodec = potential;
+                        results.encodedBand = encoded2;
+                        results.saved = saved;
+                    }
+                }
+            } else if (potential.largest() >= bandData.largest
+                    && potential.smallest() <= bandData.smallest) {
+                byte[] encoded2 = potential.encode(band);
+                results.numCodecsTried++;
+                byte[] specifierEncoded = defaultCodec.encode(CodecEncoding
+                        .getSpecifier(potential, null));
+                int saved = encoded.length - encoded2.length
+                        - specifierEncoded.length;
+                if (saved > results.saved) {
+                    results.betterCodec = potential;
+                    results.encodedBand = encoded2;
+                    results.saved = saved;
+                }
+            }
+            if(timeToStop(results)) {
+                return;
+            }
+        }
+    }
+
+// This could be useful if further enhancements are done but is not currently used
+//
+//    private void encodeWithRunCodec(String name, int[] band, int index,
+//            BHSDCodec defaultCodec, BandData bandData,
+//            BandAnalysisResults results) throws Pack200Exception {
+//        int[] firstBand = new int[index];
+//        int[] secondBand = new int[band.length - index];
+//        System.arraycopy(band, 0, firstBand, 0, index);
+//        System.arraycopy(band, index, secondBand, 0, secondBand.length);
+//        BandAnalysisResults firstResults = analyseBand(name + "A", firstBand, defaultCodec);
+//        BandAnalysisResults secondResults = analyseBand(name + "B", secondBand, defaultCodec);
+//        int specifier = 117;
+//        byte[] specifierEncoded = defaultCodec.encode(new int[] {specifier});
+//        int totalLength = firstResults.encodedBand.length + secondResults.encodedBand.length + specifierEncoded.length + 4; // TODO actual
+//        if(totalLength < results.encodedBand.length) {
+//            System.out.println("using run codec");
+//            results.saved += results.encodedBand.length - totalLength;
+//            byte[] encodedBand = new byte[specifierEncoded.length + firstResults.encodedBand.length + secondResults.encodedBand.length];
+//            System.arraycopy(specifierEncoded, 0, encodedBand, 0, specifierEncoded.length);
+//            System.arraycopy(firstResults.encodedBand, 0, encodedBand, specifierEncoded.length, firstResults.encodedBand.length);
+//            System.arraycopy(secondResults.encodedBand, 0, encodedBand, specifierEncoded.length + firstResults.encodedBand.length, secondResults.encodedBand.length);
+//            results.encodedBand = encodedBand;
+//            results.betterCodec = new RunCodec(index, firstResults.betterCodec, secondResults.betterCodec);
+//        }
+//    }
+
+    private void encodeWithPopulationCodec(String name, int[] band,
+            BHSDCodec defaultCodec, BandData bandData, BandAnalysisResults results) throws Pack200Exception {
+        results.numCodecsTried += 3; // quite a bit more effort to try this codec
+        final Map distinctValues = bandData.distinctValues;
+
+        List favoured = new ArrayList();
+        for (Iterator iterator = distinctValues.keySet().iterator(); iterator
+                .hasNext();) {
+            Integer value = (Integer) iterator.next();
+            Integer count = (Integer) distinctValues.get(value);
+            if(count.intValue() > 2 || distinctValues.size() < 256) { // TODO: tweak
+                favoured.add(value);
+            }
+        }
+
+        // Sort the favoured list with the most commonly occurring first
+        if(distinctValues.size() > 255) {
+            Collections.sort(favoured, new Comparator() {
+                public int compare(Object arg0, Object arg1) {
+                    return ((Integer)distinctValues.get(arg1)).compareTo((Integer)distinctValues.get(arg0));
+                }
+            });
+        }
+
+        IntList unfavoured = new IntList();
+        Map favouredToIndex = new HashMap();
+        for (int i = 0; i < favoured.size(); i++) {
+            Integer value = (Integer) favoured.get(i);
+            favouredToIndex.put(value, new Integer(i));
+        }
+
+        int[] tokens = new int[band.length];
+        for (int i = 0; i < band.length; i++) {
+            Integer favouredIndex = (Integer)favouredToIndex.get(new Integer(band[i]));
+            if(favouredIndex == null) {
+                tokens[i] = 0;
+                unfavoured.add(band[i]);
+            } else {
+                tokens[i] = favouredIndex.intValue() + 1;
+            }
+        }
+        favoured.add(favoured.get(favoured.size() - 1)); // repeat last value
+        int[] favouredBand = integerListToArray(favoured);
+        int[] unfavouredBand = unfavoured.toArray();
+
+        // Analyse the three bands to get the best codec
+        BandAnalysisResults favouredResults = analyseBand("POPULATION", favouredBand, defaultCodec);
+        BandAnalysisResults unfavouredResults = analyseBand("POPULATION", unfavouredBand, defaultCodec);
+
+        int tdefL = 0;
+        int l = 0;
+        Codec tokenCodec = null;
+        byte[] tokensEncoded;
+        int k = favoured.size() - 1;
+        if(k < 256) {
+            tdefL = 1;
+            tokensEncoded = Codec.BYTE1.encode(tokens);
+        } else {
+            BandAnalysisResults tokenResults = analyseBand("POPULATION", tokens, defaultCodec);
+            tokenCodec = tokenResults.betterCodec;
+            tokensEncoded = tokenResults.encodedBand;
+            if(tokenCodec == null) {
+                tokenCodec = defaultCodec;
+            }
+            l = ((BHSDCodec) tokenCodec).getL();
+            int h = ((BHSDCodec) tokenCodec).getH();
+            int s = ((BHSDCodec) tokenCodec).getS();
+            int b = ((BHSDCodec) tokenCodec).getB();
+            int d = ((BHSDCodec) tokenCodec).isDelta() ? 1 : 0;
+            if(s == 0 && d == 0) {
+                boolean canUseTDefL = true;
+                if(b > 1) {
+                    BHSDCodec oneLowerB = new BHSDCodec(b-1, h);
+                    if(oneLowerB.largest() >= k) {
+                        canUseTDefL = false;
+                    }
+                }
+                if(canUseTDefL) {
+                    switch (l) {
+                    case 4:
+                        tdefL = 1;
+                        break;
+                    case 8:
+                        tdefL = 2;
+                        break;
+                    case 16:
+                        tdefL = 3;
+                        break;
+                    case 32:
+                        tdefL = 4;
+                        break;
+                    case 64:
+                        tdefL = 5;
+                        break;
+                    case 128:
+                        tdefL = 6;
+                        break;
+                    case 192:
+                        tdefL = 7;
+                        break;
+                    case 224:
+                        tdefL = 8;
+                        break;
+                    case 240:
+                        tdefL = 9;
+                        break;
+                    case 248:
+                        tdefL = 10;
+                        break;
+                    case 252:
+                        tdefL = 11;
+                        break;
+                    }
+                }
+            }
+        }
+
+        byte[] favouredEncoded = favouredResults.encodedBand;
+        byte[] unfavouredEncoded = unfavouredResults.encodedBand;
+        Codec favouredCodec = favouredResults.betterCodec;
+        Codec unfavouredCodec = unfavouredResults.betterCodec;
+
+        int specifier = 141 + (favouredCodec == null ? 1 : 0) + (4 * tdefL) + (unfavouredCodec == null ? 2 : 0);
+        IntList extraBandMetadata = new IntList(3);
+        if(favouredCodec != null) {
+            int[] specifiers = CodecEncoding.getSpecifier(favouredCodec, null);
+            for (int i = 0; i < specifiers.length; i++) {
+                extraBandMetadata.add(specifiers[i]);
+            }
+        }
+        if(tdefL == 0) {
+            int[] specifiers = CodecEncoding.getSpecifier(tokenCodec, null);
+            for (int i = 0; i < specifiers.length; i++) {
+                extraBandMetadata.add(specifiers[i]);
+            }
+        }
+        if(unfavouredCodec != null) {
+            int[] specifiers = CodecEncoding.getSpecifier(unfavouredCodec, null);
+            for (int i = 0; i < specifiers.length; i++) {
+                extraBandMetadata.add(specifiers[i]);
+            }
+        }
+        int[] extraMetadata = extraBandMetadata.toArray();
+        byte[] extraMetadataEncoded = Codec.UNSIGNED5.encode(extraMetadata);
+        if(defaultCodec.isSigned()) {
+            specifier = -1 -specifier;
+        } else {
+            specifier = specifier + defaultCodec.getL();
+        }
+        byte[] firstValueEncoded = defaultCodec.encode(new int[] {specifier});
+        int totalBandLength = firstValueEncoded.length + favouredEncoded.length + tokensEncoded.length + unfavouredEncoded.length;
+
+        if(totalBandLength + extraMetadataEncoded.length < results.encodedBand.length) {
+            results.saved += results.encodedBand.length - (totalBandLength + extraMetadataEncoded.length);
+            byte[] encodedBand = new byte[totalBandLength];
+            System.arraycopy(firstValueEncoded, 0, encodedBand, 0, firstValueEncoded.length);
+            System.arraycopy(favouredEncoded, 0, encodedBand, firstValueEncoded.length, favouredEncoded.length);
+            System.arraycopy(tokensEncoded, 0, encodedBand, firstValueEncoded.length + favouredEncoded.length, tokensEncoded.length);
+            System.arraycopy(unfavouredEncoded, 0, encodedBand, firstValueEncoded.length + favouredEncoded.length + tokensEncoded.length, unfavouredEncoded.length);
+            results.encodedBand = encodedBand;
+            results.extraMetadata = extraMetadata;
+            if(l != 0) {
+                results.betterCodec = new PopulationCodec(favouredCodec, l, unfavouredCodec);
+            } else {
+                results.betterCodec = new PopulationCodec(favouredCodec, tokenCodec, unfavouredCodec);
+            }
+        }
+    }
+
+    /**
+     * Encode a band of longs (values are split into their high and low 32 bits
+     * and then encoded as two separate bands
+     *
+     * @param name
+     *            - name of the band (for debugging purposes)
+     * @param flags
+     *            - the band
+     * @param loCodec
+     *            - Codec for the low 32-bits band
+     * @param hiCodec
+     *            - Codec for the high 32-bits band
+     * @param haveHiFlags
+     *            - ignores the high band if true as all values would be zero
+     * @return the encoded band
+     * @throws Pack200Exception
+     */
+    protected byte[] encodeFlags(String name, long[] flags, BHSDCodec loCodec, BHSDCodec hiCodec,
+            boolean haveHiFlags) throws Pack200Exception {
+        if(!haveHiFlags) {
+            int[] loBits = new int[flags.length];
+            for (int i = 0; i < flags.length; i++) {
+                loBits[i] = (int) flags[i];
+            }
+            return encodeBandInt(name, loBits, loCodec);
+        } else {
+
+            int[] hiBits = new int[flags.length];
+            int[] loBits = new int[flags.length];
+            for (int i = 0; i < flags.length; i++) {
+                long l = flags[i];
+                hiBits[i] = (int) (l >> 32);
+                loBits[i] = (int) l;
+            }
+            byte[] hi = encodeBandInt(name, hiBits, hiCodec);
+            byte[] lo = encodeBandInt(name, loBits, loCodec);
+            byte[] total = new byte[hi.length + lo.length];
+            System.arraycopy(hi, 0, total, 0, hi.length);
+            System.arraycopy(lo, 0, total, hi.length + 1, lo.length);
+            return total;
+        }
+    }
+
+    /**
+     * Converts a list of Integers to an int[] array
+     */
+    protected int[] integerListToArray(List integerList) {
+        int[] array = new int[integerList.size()];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = ((Integer)integerList.get(i)).intValue();
+        }
+        return array;
+    }
+
+    /**
+     * Converts a list of Longs to an long[] array
+     */
+    protected long[] longListToArray(List longList) {
+        long[] array = new long[longList.size()];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = ((Long)longList.get(i)).longValue();
+        }
+        return array;
+    }
+
+    /**
+     * Converts a list of ConstantPoolEntrys to an int[] array of their indices
+     */
+    protected int[] cpEntryListToArray(List list) {
+        int[] array = new int[list.size()];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = ((ConstantPoolEntry)list.get(i)).getIndex();
+            if(array[i] < 0) {
+                throw new RuntimeException("Index should be > 0");
+            }
+        }
+        return array;
+    }
+
+    /**
+     * Converts a list of ConstantPoolEntrys or nulls to an int[] array of their
+     * indices +1 (or 0 for nulls)
+     */
+    protected int[] cpEntryOrNullListToArray(List theList) {
+        int[] array = new int[theList.size()];
+        for (int j = 0; j < array.length; j++) {
+            ConstantPoolEntry cpEntry = (ConstantPoolEntry) theList.get(j);
+            array[j] = cpEntry == null ? 0 : cpEntry.getIndex() + 1;
+            if(cpEntry != null && cpEntry.getIndex() < 0) {
+                throw new RuntimeException("Index should be > 0");
+            }
+        }
+        return array;
+    }
+
+    protected byte[] encodeFlags(String name, long[][] flags, BHSDCodec loCodec, BHSDCodec hiCodec,
+            boolean haveHiFlags) throws Pack200Exception {
+        return encodeFlags(name, flatten(flags), loCodec, hiCodec, haveHiFlags);
+   }
+
+    /*
+     * Flatten a 2-dimension array into a 1-dimension array
+     */
+    private long[] flatten(long[][] flags) {
+        int totalSize = 0;
+        for (int i = 0; i < flags.length; i++) {
+            totalSize += flags[i].length;
+        }
+        long[] flatArray = new long[totalSize];
+        int index = 0;
+        for (int i = 0; i < flags.length; i++) {
+            for (int j = 0; j < flags[i].length; j++) {
+                flatArray[index] = flags[i][j];
+                index++;
+            }
+        }
+        return flatArray;
+    }
+
+    /**
+     * BandData represents information about a band, e.g. largest value etc
+     * and is used in the heuristics that calculate whether an alternative
+     * Codec could make the encoded band smaller.
+     */
+    public class BandData {
+
+        private final int[] band;
+        private int smallest = Integer.MAX_VALUE;
+        private int largest = Integer.MIN_VALUE;
+        private int smallestDelta;
+        private int largestDelta;
+
+        private int deltaIsAscending = 0;
+        private int smallDeltaCount = 0;
+
+        private double averageAbsoluteDelta = 0;
+        private double averageAbsoluteValue = 0;
+
+        private Map distinctValues;
+
+        /**
+         * Create a new instance of BandData.  The band is then analysed.
+         * @param band - the band of integers
+         */
+        public BandData(int[] band) {
+            this.band = band;
+            Integer one = new Integer(1);
+            for (int i = 0; i < band.length; i++) {
+                if(band[i] < smallest) {
+                    smallest = band[i];
+                }
+                if(band[i] > largest) {
+                    largest = band[i];
+                }
+                if(i != 0) {
+                    int delta = band[i] - band[i - 1];
+                    if(delta < smallestDelta) {
+                        smallestDelta = delta;
+                    }
+                    if(delta > largestDelta) {
+                        largestDelta = delta;
+                    }
+                    if(delta >= 0) {
+                        deltaIsAscending++;
+                    }
+                    averageAbsoluteDelta += (double)Math.abs(delta)/(double)(band.length - 1);
+                    if(Math.abs(delta) < 256) {
+                        smallDeltaCount++;
+                    }
+                } else {
+                    smallestDelta = band[0];
+                    largestDelta = band[0];
+                }
+                averageAbsoluteValue += (double)Math.abs(band[i])/(double)band.length;
+                if(effort > 3) { // do calculations needed to consider population codec
+                    if(distinctValues == null) {
+                        distinctValues = new HashMap();
+                    }
+                    Integer value = new Integer(band[i]);
+                    Integer count = (Integer) distinctValues.get(value);
+                    if(count == null) {
+                        count = one;
+                    } else {
+                        count = new Integer(count.intValue() + 1);
+                    }
+                    distinctValues.put(value, count);
+                }
+            }
+        }
+
+        /**
+         * Returns true if the deltas between adjacent band elements are mainly
+         * small (heuristic)
+         */
+        public boolean mainlySmallDeltas() {
+            // Note: the value below has been tuned - please test carefully if changing it
+            return (float)smallDeltaCount/(float)band.length > 0.7F;
+        }
+
+        /**
+         * Returns true if the band is well correlated (i.e. would be suitable
+         * for a delta encoding) (heuristic)
+         */
+        public boolean wellCorrelated() {
+            // Note: the value below has been tuned - please test carefully if changing it
+            return averageAbsoluteDelta * 3.1 < averageAbsoluteValue;
+        }
+
+        /**
+         * Returns true if the band deltas are mainly positive (heuristic)
+         */
+        public boolean mainlyPositiveDeltas() {
+            // Note: the value below has been tuned - please test carefully if changing it
+            return (float)deltaIsAscending/(float)band.length > 0.95F;
+        }
+
+        /**
+         * Returns true if any band elements are negative
+         */
+        public boolean anyNegatives() {
+            return smallest < 0;
+        }
+
+        /**
+         * Returns the total number of distinct values found in the band
+         */
+        public int numDistinctValues() {
+            if(distinctValues == null) {
+                return band.length;
+            }
+            return distinctValues.size();
+        }
+
+    }
+
+    /**
+     * Results obtained by trying different Codecs to encode a band
+     */
+    public class BandAnalysisResults {
+
+        // The number of Codecs tried so far
+        private int numCodecsTried = 0;
+
+        // The number of bytes saved by using betterCodec instead of the default codec
+        private int saved = 0;
+
+        // Extra metadata to pass to the segment header (to be appended to the
+        // band_headers band)
+        private int[] extraMetadata;
+
+        // The results of encoding the band with betterCodec
+        private byte[] encodedBand;
+
+        // The best Codec found so far, or should be null if the default is the
+        // best so far
+        private Codec betterCodec;
+
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/BcBands.java b/src/main/java/org/apache/harmony/pack200/BcBands.java
new file mode 100644
index 0000000..92c65fe
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/BcBands.java
@@ -0,0 +1,583 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Label;
+
+/**
+ * Bytecode bands (corresponds to the <code>bc_bands</code> set of bands in the
+ * pack200 specification)
+ */
+public class BcBands extends BandSet {
+
+    private final CpBands cpBands;
+    private final Segment segment;
+
+    public BcBands(CpBands cpBands, Segment segment, int effort) {
+        super(effort, segment.getSegmentHeader());
+        this.cpBands = cpBands;
+        this.segment = segment;
+    }
+
+    private final IntList bcCodes = new IntList();
+    private final IntList bcCaseCount = new IntList();
+    private final IntList bcCaseValue = new IntList();
+    private final IntList bcByte = new IntList();
+    private final IntList bcShort = new IntList();
+    private final IntList bcLocal = new IntList();
+    private final List bcLabel = new ArrayList();
+    private final List bcIntref = new ArrayList();
+    private final List bcFloatRef = new ArrayList();
+    private final List bcLongRef = new ArrayList();
+    private final List bcDoubleRef = new ArrayList();
+    private final List bcStringRef = new ArrayList();
+    private final List bcClassRef = new ArrayList();
+    private final List bcFieldRef = new ArrayList();
+    private final List bcMethodRef = new ArrayList();
+    private final List bcIMethodRef = new ArrayList();
+    private List bcThisField = new ArrayList();
+    private final List bcSuperField = new ArrayList();
+    private List bcThisMethod = new ArrayList();
+    private List bcSuperMethod = new ArrayList();
+    private List bcInitRef = new ArrayList();
+
+    private String currentClass;
+    private String superClass;
+    private String currentNewClass;
+
+    private static final int MULTIANEWARRAY = 197;
+    private static final int ALOAD_0 = 42;
+    private static final int WIDE = 196;
+    private static final int INVOKEINTERFACE = 185;
+    private static final int TABLESWITCH = 170;
+    private static final int IINC = 132;
+    private static final int LOOKUPSWITCH = 171;
+    private static final int endMarker = 255;
+
+    private final IntList bciRenumbering = new IntList();
+    private final Map labelsToOffsets = new HashMap();
+    private int byteCodeOffset;
+    private int renumberedOffset;
+    private final IntList bcLabelRelativeOffsets = new IntList();
+
+    public void setCurrentClass(String name, String superName) {
+        currentClass = name;
+        superClass = superName;
+    }
+
+    /**
+     * All input classes for the segment have now been read in, so this method
+     * is called so that this class can calculate/complete anything it could not
+     * do while classes were being read.
+     */
+    public void finaliseBands() {
+        bcThisField = getIndexInClass(bcThisField);
+        bcThisMethod = getIndexInClass(bcThisMethod);
+        bcSuperMethod = getIndexInClass(bcSuperMethod);
+        bcInitRef = getIndexInClassForConstructor(bcInitRef);
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing byte code bands...");
+        byte[] encodedBand = encodeBandInt("bcCodes", bcCodes.toArray(),
+                Codec.BYTE1);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCodes["
+                + bcCodes.size() + "]");
+
+        encodedBand = encodeBandInt("bcCaseCount", bcCaseCount.toArray(),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcCaseCount[" + bcCaseCount.size() + "]");
+
+        encodedBand = encodeBandInt("bcCaseValue", bcCaseValue.toArray(),
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcCaseValue[" + bcCaseValue.size() + "]");
+
+        encodedBand = encodeBandInt("bcByte", bcByte.toArray(), Codec.BYTE1);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcByte["
+                + bcByte.size() + "]");
+
+        encodedBand = encodeBandInt("bcShort", bcShort.toArray(), Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcShort["
+                + bcShort.size() + "]");
+
+        encodedBand = encodeBandInt("bcLocal", bcLocal.toArray(),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLocal["
+                + bcLocal.size() + "]");
+
+        encodedBand = encodeBandInt("bcLabel", integerListToArray(bcLabel),
+                Codec.BRANCH5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLabel["
+                + bcLabel.size() + "]");
+
+        encodedBand = encodeBandInt("bcIntref", cpEntryListToArray(bcIntref),
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcIntref[" + bcIntref.size() + "]");
+
+        encodedBand = encodeBandInt("bcFloatRef",
+                cpEntryListToArray(bcFloatRef), Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcFloatRef[" + bcFloatRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcLongRef", cpEntryListToArray(bcLongRef),
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcLongRef[" + bcLongRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcDoubleRef",
+                cpEntryListToArray(bcDoubleRef), Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcDoubleRef[" + bcDoubleRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcStringRef",
+                cpEntryListToArray(bcStringRef), Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcStringRef[" + bcStringRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcClassRef",
+                cpEntryOrNullListToArray(bcClassRef), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcClassRef[" + bcClassRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcFieldRef",
+                cpEntryListToArray(bcFieldRef), Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcFieldRef[" + bcFieldRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcMethodRef",
+                cpEntryListToArray(bcMethodRef), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcMethodRef[" + bcMethodRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcIMethodRef",
+                cpEntryListToArray(bcIMethodRef), Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcIMethodRef[" + bcIMethodRef.size() + "]");
+
+        encodedBand = encodeBandInt("bcThisField",
+                integerListToArray(bcThisField), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcThisField[" + bcThisField.size() + "]");
+
+        encodedBand = encodeBandInt("bcSuperField",
+                integerListToArray(bcSuperField), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcSuperField[" + bcSuperField.size() + "]");
+
+        encodedBand = encodeBandInt("bcThisMethod",
+                integerListToArray(bcThisMethod), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcThisMethod[" + bcThisMethod.size() + "]");
+
+        encodedBand = encodeBandInt("bcSuperMethod",
+                integerListToArray(bcSuperMethod), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcSuperMethod[" + bcSuperMethod.size() + "]");
+
+        encodedBand = encodeBandInt("bcInitRef", integerListToArray(bcInitRef),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from bcInitRef[" + bcInitRef.size() + "]");
+
+        // out.write(encodeBandInt(cpEntryintegerListToArray(bcEscRef),
+        // Codec.UNSIGNED5));
+        // out.write(encodeBandInt(integerListToArray(bcEscRefSize),
+        // Codec.UNSIGNED5));
+        // out.write(encodeBandInt(integerListToArray(bcEscSize),
+        // Codec.UNSIGNED5));
+        // out.write(encodeBandInt(integerListToArray(bcEscByte), Codec.BYTE1));
+    }
+
+    private List getIndexInClass(List cPMethodOrFieldList) {
+        List indices = new ArrayList(cPMethodOrFieldList.size());
+        for (int i = 0; i < cPMethodOrFieldList.size(); i++) {
+            CPMethodOrField cpMF = (CPMethodOrField) cPMethodOrFieldList.get(i);
+            indices.add(new Integer(cpMF.getIndexInClass()));
+        }
+        return indices;
+    }
+
+    private List getIndexInClassForConstructor(List cPMethodList) {
+        List indices = new ArrayList(cPMethodList.size());
+        for (int i = 0; i < cPMethodList.size(); i++) {
+            CPMethodOrField cpMF = (CPMethodOrField) cPMethodList.get(i);
+            indices.add(new Integer(cpMF.getIndexInClassForConstructor()));
+        }
+        return indices;
+    }
+
+    public void visitEnd() {
+        for (int i = 0; i < bciRenumbering.size(); i++) {
+            if (bciRenumbering.get(i) == -1) {
+                bciRenumbering.remove(i);
+                bciRenumbering.add(i, ++renumberedOffset);
+            }
+        }
+        if (renumberedOffset != 0) {
+            if(renumberedOffset + 1 != bciRenumbering.size()) {
+                throw new RuntimeException("Mistake made with renumbering");
+            }
+            for (int i = bcLabel.size() - 1; i >= 0; i--) {
+                Object label = bcLabel.get(i);
+                if (label instanceof Integer) {
+                    break;
+                } else if (label instanceof Label) {
+                    bcLabel.remove(i);
+                    Integer offset = (Integer) labelsToOffsets.get(label);
+                    int relativeOffset = bcLabelRelativeOffsets.get(i);
+                    bcLabel.add(i, new Integer(bciRenumbering.get(offset.intValue()) - bciRenumbering.get(relativeOffset)));
+                }
+            }
+            bcCodes.add(endMarker);
+            segment.getClassBands().doBciRenumbering(bciRenumbering,
+                    labelsToOffsets);
+            bciRenumbering.clear();
+            labelsToOffsets.clear();
+            byteCodeOffset = 0;
+            renumberedOffset = 0;
+        }
+    }
+
+    public void visitLabel(Label label) {
+        labelsToOffsets.put(label, new Integer(byteCodeOffset));
+    }
+
+    public void visitFieldInsn(int opcode, String owner, String name,
+            String desc) {
+        byteCodeOffset += 3;
+        updateRenumbering();
+        boolean aload_0 = false;
+        if (bcCodes.size() > 0
+                && (bcCodes.get(bcCodes.size() - 1)) == ALOAD_0) {
+            bcCodes.remove(bcCodes.size() - 1);
+            aload_0 = true;
+        }
+        CPMethodOrField cpField = cpBands.getCPField(owner, name, desc);
+        if (aload_0) {
+            opcode += 7;
+        }
+        if (owner.equals(currentClass)) {
+            opcode += 24; // change to getstatic_this, putstatic_this etc.
+            bcThisField.add(cpField);
+//        } else if (owner.equals(superClass)) {
+//            opcode += 38; // change to getstatic_super etc.
+//            bcSuperField.add(cpField);
+        } else {
+            if (aload_0) {
+                opcode -= 7;
+                bcCodes.add(ALOAD_0); // add aload_0 back in because
+                // there's no special rewrite in
+                // this case.
+            }
+            bcFieldRef.add(cpField);
+        }
+        aload_0 = false;
+        bcCodes.add(opcode);
+    }
+
+    private void updateRenumbering() {
+        if(bciRenumbering.isEmpty()) {
+            bciRenumbering.add(0);
+        }
+        renumberedOffset ++;
+        for (int i = bciRenumbering.size(); i < byteCodeOffset; i++) {
+            bciRenumbering.add(-1);
+        }
+        bciRenumbering.add(renumberedOffset);
+    }
+
+    public void visitIincInsn(int var, int increment) {
+        if (var > 255 || increment > 255) {
+            byteCodeOffset += 6;
+            bcCodes.add(WIDE);
+            bcCodes.add(IINC);
+            bcLocal.add(var);
+            bcShort.add(increment);
+        } else {
+            byteCodeOffset += 3;
+            bcCodes.add(IINC);
+            bcLocal.add(var);
+            bcByte.add(increment & 0xFF);
+        }
+        updateRenumbering();
+    }
+
+    public void visitInsn(int opcode) {
+        if (opcode >= 202) {
+            throw new RuntimeException(
+                    "Non-standard bytecode instructions not supported");
+        } else {
+            bcCodes.add(opcode);
+            byteCodeOffset++;
+            updateRenumbering();
+        }
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+        switch (opcode) {
+        case 17: // sipush
+            bcCodes.add(opcode);
+            bcShort.add(operand);
+            byteCodeOffset += 3;
+            break;
+        case 16: // bipush
+        case 188: // newarray
+            bcCodes.add(opcode);
+            bcByte.add(operand & 0xFF);
+            byteCodeOffset += 2;
+        }
+        updateRenumbering();
+    }
+
+    public void visitJumpInsn(int opcode, Label label) {
+        bcCodes.add(opcode);
+        bcLabel.add(label);
+        bcLabelRelativeOffsets.add(byteCodeOffset);
+        byteCodeOffset += 3;
+        updateRenumbering();
+    }
+
+    public void visitLdcInsn(Object cst) {
+        CPConstant constant = cpBands.getConstant(cst);
+        if (segment.lastConstantHadWideIndex() || constant instanceof CPLong
+                || constant instanceof CPDouble) {
+            byteCodeOffset += 3;
+            if (constant instanceof CPInt) {
+                bcCodes.add(237); // ildc_w
+                bcIntref.add(constant);
+            } else if (constant instanceof CPFloat) {
+                bcCodes.add(238); // fldc
+                bcFloatRef.add(constant);
+            } else if (constant instanceof CPLong) {
+                bcCodes.add(20); // lldc2_w
+                bcLongRef.add(constant);
+            } else if (constant instanceof CPDouble) {
+                bcCodes.add(239); // dldc2_w
+                bcDoubleRef.add(constant);
+            } else if (constant instanceof CPString) {
+                bcCodes.add(19); // aldc
+                bcStringRef.add(constant);
+            } else if (constant instanceof CPClass) {
+                bcCodes.add(236); // cldc
+                bcClassRef.add(constant);
+            } else {
+                throw new RuntimeException("Constant should not be null");
+            }
+        } else {
+            byteCodeOffset += 2;
+            if (constant instanceof CPInt) {
+                bcCodes.add(234); // ildc
+                bcIntref.add(constant);
+            } else if (constant instanceof CPFloat) {
+                bcCodes.add(235); // fldc
+                bcFloatRef.add(constant);
+            } else if (constant instanceof CPString) {
+                bcCodes.add(18); // aldc
+                bcStringRef.add(constant);
+            } else if (constant instanceof CPClass) {
+                bcCodes.add(233); // cldc
+                bcClassRef.add(constant);
+            }
+        }
+        updateRenumbering();
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        bcCodes.add(LOOKUPSWITCH);
+        bcLabel.add(dflt);
+        bcLabelRelativeOffsets.add(byteCodeOffset);
+        bcCaseCount.add(keys.length);
+        for (int i = 0; i < labels.length; i++) {
+            bcCaseValue.add(keys[i]);
+            bcLabel.add(labels[i]);
+            bcLabelRelativeOffsets.add(byteCodeOffset);
+        }
+        int padding = (byteCodeOffset + 1) % 4 == 0 ? 0 : 4 - ((byteCodeOffset + 1) % 4);
+        byteCodeOffset += 1 + padding + 8 + 8 * keys.length;
+        updateRenumbering();
+    }
+
+    public void visitMethodInsn(int opcode, String owner, String name,
+            String desc) {
+        byteCodeOffset += 3;
+        switch (opcode) {
+        case 182: // invokevirtual
+        case 183: // invokespecial
+        case 184: // invokestatic
+            boolean aload_0 = false;
+            if (bcCodes.size() > 0
+                    && (bcCodes.get(bcCodes.size() - 1))
+                             == (ALOAD_0)) {
+                bcCodes.remove(bcCodes.size() - 1);
+                aload_0 = true;
+                opcode += 7;
+            }
+            if (owner.equals(currentClass)) {
+                opcode += 24; // change to invokevirtual_this,
+                // invokespecial_this etc.
+
+                if(name.equals("<init>") && opcode == 207) {
+                    opcode = 230; // invokespecial_this_init
+                    bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
+                } else {
+                    bcThisMethod.add(cpBands.getCPMethod(owner, name, desc));
+                }
+            } else if (owner.equals(superClass)) { // TODO
+                opcode += 38; // change to invokevirtual_super,
+                // invokespecial_super etc.
+                if(name.equals("<init>") && opcode == 221) {
+                    opcode = 231; // invokespecial_super_init
+                    bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
+                } else {
+                    bcSuperMethod.add(cpBands.getCPMethod(owner, name, desc));
+                }
+            } else {
+                if (aload_0) {
+                    opcode -= 7;
+                    bcCodes.add(ALOAD_0); // add aload_0 back in
+                    // because there's no
+                    // special rewrite in this
+                    // case.
+                }
+                if(name.equals("<init>") && opcode == 183 && owner.equals(currentNewClass)) {
+                    opcode = 232; // invokespecial_new_init
+                    bcInitRef.add(cpBands.getCPMethod(owner, name, desc));
+                } else {
+                    bcMethodRef.add(cpBands.getCPMethod(owner, name, desc));
+                }
+            }
+            bcCodes.add(opcode);
+            break;
+        case 185: // invokeinterface
+            byteCodeOffset += 2;
+            CPMethodOrField cpIMethod = cpBands.getCPIMethod(owner, name, desc);
+            bcIMethodRef.add(cpIMethod);
+            bcCodes.add(INVOKEINTERFACE);
+            break;
+        }
+        updateRenumbering();
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dimensions) {
+        byteCodeOffset += 4;
+        updateRenumbering();
+        bcCodes.add(MULTIANEWARRAY);
+        bcClassRef.add(cpBands.getCPClass(desc));
+        bcByte.add(dimensions & 0xFF);
+    }
+
+    public void visitTableSwitchInsn(int min, int max, Label dflt,
+            Label[] labels) {
+        bcCodes.add(TABLESWITCH);
+        bcLabel.add(dflt);
+        bcLabelRelativeOffsets.add(byteCodeOffset);
+        bcCaseValue.add(min);
+        int count = labels.length;
+        bcCaseCount.add(count);
+        for (int i = 0; i < count; i++) {
+            bcLabel.add(labels[i]);
+            bcLabelRelativeOffsets.add(byteCodeOffset);
+        }
+        int padding = byteCodeOffset % 4 == 0 ? 0 : 4 - (byteCodeOffset % 4);
+        byteCodeOffset+= (padding + 12 + 4 * labels.length);
+        updateRenumbering();
+    }
+
+    public void visitTypeInsn(int opcode, String type) {
+        // NEW, ANEWARRAY, CHECKCAST or INSTANCEOF
+        byteCodeOffset += 3;
+        updateRenumbering();
+        bcCodes.add(opcode);
+        bcClassRef.add(cpBands.getCPClass(type));
+        if(opcode == 187) { // NEW
+            currentNewClass = type;
+        }
+    }
+
+    public void visitVarInsn(int opcode, int var) {
+        // ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET
+        if (var > 255) {
+            byteCodeOffset += 4;
+            bcCodes.add(WIDE);
+            bcCodes.add(opcode);
+            bcLocal.add(var);
+        } else {
+            if(var > 3 || opcode == 169 /* RET */) {
+                byteCodeOffset += 2;
+                bcCodes.add(opcode);
+                bcLocal.add(var);
+            } else {
+                byteCodeOffset +=1;
+                switch(opcode) {
+                case 21: // ILOAD
+                case 54: // ISTORE
+                    bcCodes.add(opcode + 5 + var);
+                    break;
+                case 22: // LLOAD
+                case 55: // LSTORE
+                    bcCodes.add(opcode + 8 + var);
+                    break;
+                case 23: // FLOAD
+                case 56: // FSTORE
+                    bcCodes.add(opcode + 11 + var);
+                    break;
+                case 24: // DLOAD
+                case 57: // DSTORE
+                    bcCodes.add(opcode + 14 + var);
+                    break;
+                case 25: // A_LOAD
+                case 58: // A_STORE
+                    bcCodes.add(opcode + 17 + var);
+                    break;
+                }
+            }
+        }
+        updateRenumbering();
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPClass.java b/src/main/java/org/apache/harmony/pack200/CPClass.java
new file mode 100644
index 0000000..1639613
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPClass.java
@@ -0,0 +1,57 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a class
+ */
+public class CPClass extends CPConstant implements Comparable {
+
+    private final String className;
+    private final CPUTF8 utf8;
+    private final boolean isInnerClass;
+
+    public CPClass(CPUTF8 utf8) {
+        this.utf8 = utf8;
+        this.className = utf8.getUnderlyingString();
+        char[] chars = className.toCharArray();
+        for (int i = 0; i < chars.length; i++) {
+            if(chars[i] <= 0x2D) {
+                isInnerClass = true;
+                return;
+            }
+        }
+        isInnerClass = false;
+    }
+
+    public int compareTo(Object arg0) {
+        return className.compareTo(((CPClass)arg0).className);
+    }
+
+    public String toString() {
+        return className;
+    }
+
+    public int getIndexInCpUtf8() {
+        return utf8.getIndex();
+    }
+
+    public boolean isInnerClass() {
+        return isInnerClass;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPConstant.java b/src/main/java/org/apache/harmony/pack200/CPConstant.java
new file mode 100644
index 0000000..ffd3229
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPConstant.java
@@ -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.harmony.pack200;
+
+/**
+ * Abstract superclass for constant pool constant entries such as numbers or
+ * Strings
+ */
+public abstract class CPConstant extends ConstantPoolEntry implements
+        Comparable {
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPDouble.java b/src/main/java/org/apache/harmony/pack200/CPDouble.java
new file mode 100644
index 0000000..210f774
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPDouble.java
@@ -0,0 +1,37 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a double.
+ */
+public class CPDouble extends CPConstant {
+
+    private final double theDouble;
+
+    public CPDouble(double theDouble) {
+        this.theDouble = theDouble;
+    }
+
+    public int compareTo(Object obj) {
+        return Double.compare(theDouble, ((CPDouble)obj).theDouble);
+    }
+
+    public double getDouble() {
+        return theDouble;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPFloat.java b/src/main/java/org/apache/harmony/pack200/CPFloat.java
new file mode 100644
index 0000000..6cb649d
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPFloat.java
@@ -0,0 +1,37 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a float.
+ */
+public class CPFloat extends CPConstant {
+
+    private final float theFloat;
+
+    public CPFloat(float theFloat) {
+        this.theFloat = theFloat;
+    }
+
+    public int compareTo(Object obj) {
+        return Float.compare(theFloat, ((CPFloat)obj).theFloat);
+    }
+
+    public float getFloat() {
+        return theFloat;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPInt.java b/src/main/java/org/apache/harmony/pack200/CPInt.java
new file mode 100644
index 0000000..abcfe30
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPInt.java
@@ -0,0 +1,43 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for an int.
+ */
+public class CPInt extends CPConstant {
+
+    private final int theInt;
+
+    public CPInt(int theInt) {
+        this.theInt = theInt;
+    }
+
+    public int compareTo(Object obj) {
+        if(theInt > ((CPInt)obj).theInt) {
+            return 1;
+        } else if (theInt == ((CPInt)obj).theInt) {
+            return 0;
+        } else {
+            return -1;
+        }
+    }
+
+    public int getInt() {
+        return theInt;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPLong.java b/src/main/java/org/apache/harmony/pack200/CPLong.java
new file mode 100644
index 0000000..2f2d7b4
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPLong.java
@@ -0,0 +1,48 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a long.
+ */
+public class CPLong extends CPConstant {
+
+    private final long theLong;
+
+    public CPLong(long theLong) {
+        this.theLong = theLong;
+    }
+
+    public int compareTo(Object obj) {
+        if(theLong > ((CPLong)obj).theLong) {
+            return 1;
+        } else if (theLong == ((CPLong)obj).theLong) {
+            return 0;
+        } else {
+            return -1;
+        }
+    }
+
+    public long getLong() {
+        return theLong;
+    }
+
+    public String toString() {
+        return "" + theLong;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java b/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java
new file mode 100644
index 0000000..7c1c937
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java
@@ -0,0 +1,83 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a method or field.
+ */
+public class CPMethodOrField extends ConstantPoolEntry implements Comparable {
+
+    private final CPClass className;
+    private final CPNameAndType nameAndType;
+    private int indexInClass = -1;
+    private int indexInClassForConstructor = -1;
+
+    public CPMethodOrField(CPClass className, CPNameAndType nameAndType) {
+        this.className = className;
+        this.nameAndType = nameAndType;
+    }
+
+    public String toString() {
+        return className + ": " + nameAndType;
+    }
+
+    public int compareTo(Object obj) {
+        if (obj instanceof CPMethodOrField) {
+            CPMethodOrField mof = (CPMethodOrField) obj;
+            int compareName = className.compareTo(mof.className);
+            if (compareName == 0) {
+                return nameAndType.compareTo(mof.nameAndType);
+            } else {
+                return compareName;
+            }
+        }
+        return 0;
+    }
+
+    public int getClassIndex() {
+        return className.getIndex();
+    }
+
+    public CPClass getClassName() {
+        return className;
+    }
+
+    public int getDescIndex() {
+        return nameAndType.getIndex();
+    }
+
+    public CPNameAndType getDesc() {
+        return nameAndType;
+    }
+
+    public int getIndexInClass() {
+        return indexInClass;
+    }
+
+    public void setIndexInClass(int index) {
+        indexInClass = index;
+    }
+
+    public int getIndexInClassForConstructor() {
+        return indexInClassForConstructor;
+    }
+
+    public void setIndexInClassForConstructor(int index) {
+        indexInClassForConstructor = index;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/harmony/pack200/CPNameAndType.java b/src/main/java/org/apache/harmony/pack200/CPNameAndType.java
new file mode 100644
index 0000000..52c3985
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPNameAndType.java
@@ -0,0 +1,61 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a name and type pair.
+ */
+public class CPNameAndType extends ConstantPoolEntry implements Comparable {
+
+    private final CPUTF8 name;
+    private final CPSignature signature;
+
+    public CPNameAndType(CPUTF8 name, CPSignature signature) {
+        this.name = name;
+        this.signature = signature;
+    }
+
+    public String toString() {
+        return name + ":" + signature;
+    }
+
+    public int compareTo(Object obj) {
+        if (obj instanceof CPNameAndType) {
+            CPNameAndType nat = (CPNameAndType) obj;
+            int compareSignature = signature.compareTo(nat.signature);;
+            if(compareSignature == 0) {
+                return name.compareTo(nat.name);
+            } else {
+                return compareSignature;
+            }
+        }
+        return 0;
+    }
+
+    public int getNameIndex() {
+        return name.getIndex();
+    }
+
+    public String getName() {
+        return name.getUnderlyingString();
+    }
+
+    public int getTypeIndex() {
+        return signature.getIndex();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/harmony/pack200/CPSignature.java b/src/main/java/org/apache/harmony/pack200/CPSignature.java
new file mode 100644
index 0000000..d5e423c
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPSignature.java
@@ -0,0 +1,86 @@
+/*
+ *  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.harmony.pack200;
+
+import java.util.List;
+
+/**
+ * Constant pool entry for a signature.
+ */
+public class CPSignature extends ConstantPoolEntry implements Comparable {
+
+    private final CPUTF8 signatureForm;
+    private final List classes;
+    private final String signature;
+    private final boolean formStartsWithBracket;
+
+    public CPSignature(String signature, CPUTF8 signatureForm, List classes) {
+        this.signature = signature;
+        this.signatureForm = signatureForm;
+        this.classes = classes;
+        formStartsWithBracket = signatureForm.toString().startsWith("(");
+    }
+
+    public int compareTo(Object arg0) {
+        if(signature.equals(((CPSignature) arg0).signature)) {
+            return 0;
+        }
+        if (formStartsWithBracket
+                && !((CPSignature) arg0).formStartsWithBracket) {
+            return 1;
+        }
+        if (((CPSignature) arg0).formStartsWithBracket
+                && !formStartsWithBracket) {
+            return -1;
+        }
+        if (classes.size() - ((CPSignature) arg0).classes.size() != 0) {
+            return classes.size() - ((CPSignature) arg0).classes.size();
+        }
+        if (classes.size() > 0) {
+            for (int i = classes.size() - 1; i >=0; i--) {
+                CPClass cpClass = (CPClass) classes.get(i);
+                CPClass compareClass = (CPClass) ((CPSignature) arg0).classes
+                        .get(i);
+                int classComp = cpClass.compareTo(compareClass);
+                if(classComp != 0) {
+                    return classComp;
+                }
+            }
+        }
+        return signature.compareTo(((CPSignature) arg0).signature);
+    }
+
+    public int getIndexInCpUtf8() {
+        return signatureForm.getIndex();
+    }
+
+    public List getClasses() {
+        return classes;
+    }
+
+    public String toString() {
+        return signature;
+    }
+
+    public String getUnderlyingString() {
+        return signature;
+    }
+
+    public CPUTF8 getSignatureForm() {
+        return signatureForm;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPString.java b/src/main/java/org/apache/harmony/pack200/CPString.java
new file mode 100644
index 0000000..b8acca6
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPString.java
@@ -0,0 +1,44 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a String.
+ */
+public class CPString extends CPConstant {
+
+    private final String string;
+    private final CPUTF8 utf8;
+
+    public CPString(CPUTF8 utf8) {
+        this.utf8 = utf8;
+        this.string = utf8.getUnderlyingString();
+    }
+
+    public int compareTo(Object arg0) {
+        return string.compareTo(((CPString)arg0).string);
+    }
+
+    public String toString() {
+        return string;
+    }
+
+    public int getIndexInCpUtf8() {
+        return utf8.getIndex();
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CPUTF8.java b/src/main/java/org/apache/harmony/pack200/CPUTF8.java
new file mode 100644
index 0000000..00a1526
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CPUTF8.java
@@ -0,0 +1,42 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Constant pool entry for a UTF8 entry, used for storing long Strings.
+ */
+public class CPUTF8 extends ConstantPoolEntry implements Comparable {
+
+    private final String string;
+
+    public CPUTF8(String string) {
+        this.string = string;
+    }
+
+    public int compareTo(Object arg0) {
+        return string.compareTo(((CPUTF8)arg0).string);
+    }
+
+    public String toString() {
+        return string;
+    }
+
+    public String getUnderlyingString() {
+        return string;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java b/src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java
new file mode 100644
index 0000000..174f533
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CanonicalCodecFamilies.java
@@ -0,0 +1,210 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Sets of codecs that share characteristics. Mainly used for different effort
+ * compression heuristics in BandSet.
+ */
+public class CanonicalCodecFamilies {
+
+    // Families of codecs for bands of positive integers that do not correlate
+    // well (i.e. would not benefit from delta encoding)
+
+    public static BHSDCodec[] nonDeltaUnsignedCodecs1 = new BHSDCodec[] {
+     // (1,256) is a special case and is considered separately so shouldn't be included here
+//        CodecEncoding.getCanonicalCodec(1), // (1,256)
+        CodecEncoding.getCanonicalCodec(5), // (2,256)
+        CodecEncoding.getCanonicalCodec(9), // (3,256)
+        CodecEncoding.getCanonicalCodec(13) // (4,256)
+    };
+
+    public static BHSDCodec[] nonDeltaUnsignedCodecs2 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(17), // (5,4)
+        CodecEncoding.getCanonicalCodec(20), // (5,16)
+        CodecEncoding.getCanonicalCodec(23), // (5,32)
+        CodecEncoding.getCanonicalCodec(26), // (5,64)
+        CodecEncoding.getCanonicalCodec(29) // (5,128)
+    };
+
+    public static BHSDCodec[] nonDeltaUnsignedCodecs3 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(47), // (2,192)
+        CodecEncoding.getCanonicalCodec(48), // (2,224)
+        CodecEncoding.getCanonicalCodec(49), // (2,240)
+        CodecEncoding.getCanonicalCodec(50), // (2,248)
+        CodecEncoding.getCanonicalCodec(51) // (2,252)
+    };
+
+    public static BHSDCodec[] nonDeltaUnsignedCodecs4 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(70), // (3,192)
+        CodecEncoding.getCanonicalCodec(71), // (3,224)
+        CodecEncoding.getCanonicalCodec(72), // (3,240)
+        CodecEncoding.getCanonicalCodec(73), // (3,248)
+        CodecEncoding.getCanonicalCodec(74) // (3,252)
+    };
+
+    public static BHSDCodec[] nonDeltaUnsignedCodecs5 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(93), // (4,192)
+        CodecEncoding.getCanonicalCodec(94), // (4,224)
+        CodecEncoding.getCanonicalCodec(95), // (4,240)
+        CodecEncoding.getCanonicalCodec(96), // (4,248)
+        CodecEncoding.getCanonicalCodec(97) // (4,252)
+    };
+
+    // Families of codecs for bands of positive integers that do correlate well
+    // and would benefit from delta encoding
+
+    public static BHSDCodec[] deltaUnsignedCodecs1 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(3), // (1,256,0,1)
+        CodecEncoding.getCanonicalCodec(7), // (2,256,0,1)
+        CodecEncoding.getCanonicalCodec(11), // (3,256,0,1)
+        CodecEncoding.getCanonicalCodec(15) // (4,256,0,1)
+    };
+
+    public static BHSDCodec[] deltaUnsignedCodecs2 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(32), // (5,4,0,1)
+        CodecEncoding.getCanonicalCodec(35), // (5,16,0,1)
+        CodecEncoding.getCanonicalCodec(38), // (5,32,0,1)
+        CodecEncoding.getCanonicalCodec(41), // (5,64,0,1)
+        CodecEncoding.getCanonicalCodec(44) // (5,128,0,1)
+    };
+
+    public static BHSDCodec[] deltaUnsignedCodecs3 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(52), // (2,8,0,1)
+        CodecEncoding.getCanonicalCodec(54), // (2,16,0,1)
+        CodecEncoding.getCanonicalCodec(56), // (2,32,0,1)
+        CodecEncoding.getCanonicalCodec(58), // (2,64,0,1)
+        CodecEncoding.getCanonicalCodec(60), // (2,128,0,1)
+        CodecEncoding.getCanonicalCodec(62), // (2,192,0,1)
+        CodecEncoding.getCanonicalCodec(64), // (2,224,0,1)
+        CodecEncoding.getCanonicalCodec(66), // (2,240,0,1)
+        CodecEncoding.getCanonicalCodec(68) // (2,248,0,1)
+    };
+
+    public static BHSDCodec[] deltaUnsignedCodecs4 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(75), // (3,8,0,1)
+        CodecEncoding.getCanonicalCodec(77), // (3,16,0,1)
+        CodecEncoding.getCanonicalCodec(79), // (3,32,0,1)
+        CodecEncoding.getCanonicalCodec(81), // (3,64,0,1)
+        CodecEncoding.getCanonicalCodec(83), // (3,128,0,1)
+        CodecEncoding.getCanonicalCodec(85), // (3,192,0,1)
+        CodecEncoding.getCanonicalCodec(87), // (3,224,0,1)
+        CodecEncoding.getCanonicalCodec(89), // (3,240,0,1)
+        CodecEncoding.getCanonicalCodec(91) // (3,248,0,1)
+    };
+
+    public static BHSDCodec[] deltaUnsignedCodecs5 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(98), // (4,8,0,1)
+        CodecEncoding.getCanonicalCodec(100), // (4,16,0,1)
+        CodecEncoding.getCanonicalCodec(102), // (4,32,0,1)
+        CodecEncoding.getCanonicalCodec(104), // (4,64,0,1)
+        CodecEncoding.getCanonicalCodec(106), // (4,128,0,1)
+        CodecEncoding.getCanonicalCodec(108), // (4,192,0,1)
+        CodecEncoding.getCanonicalCodec(110), // (4,224,0,1)
+        CodecEncoding.getCanonicalCodec(112), // (4,240,0,1)
+        CodecEncoding.getCanonicalCodec(114) // (4,248,0,1)
+    };
+
+    // Families of codecs for bands containing positive and negative integers
+    // that do correlate well (i.e. delta encoding is used)
+
+    public static BHSDCodec[] deltaSignedCodecs1 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(4), // (1,256,1,1)
+        CodecEncoding.getCanonicalCodec(8), // (2,256,1,1)
+        CodecEncoding.getCanonicalCodec(12), // (3,256,1,1)
+        CodecEncoding.getCanonicalCodec(16) // (4,256,1,1)
+    };
+
+    public static BHSDCodec[] deltaSignedCodecs2 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(33), // (5,4,1,1)
+        CodecEncoding.getCanonicalCodec(36), // (5,16,1,1)
+        CodecEncoding.getCanonicalCodec(39), // (5,32,1,1)
+        CodecEncoding.getCanonicalCodec(42), // (5,64,1,1)
+        CodecEncoding.getCanonicalCodec(45) // (5,128,1,1)
+    };
+
+    public static BHSDCodec[] deltaSignedCodecs3 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(53), // (2,8,1,1)
+        CodecEncoding.getCanonicalCodec(55), // (2,16,1,1)
+        CodecEncoding.getCanonicalCodec(57), // (2,32,1,1)
+        CodecEncoding.getCanonicalCodec(59), // (2,64,1,1)
+        CodecEncoding.getCanonicalCodec(61), // (2,128,1,1)
+        CodecEncoding.getCanonicalCodec(63), // (2,192,1,1)
+        CodecEncoding.getCanonicalCodec(65), // (2,224,1,1)
+        CodecEncoding.getCanonicalCodec(67), // (2,240,1,1)
+        CodecEncoding.getCanonicalCodec(69) // (2,248,1,1)
+    };
+
+    public static BHSDCodec[] deltaSignedCodecs4 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(76), // (3,8,1,1)
+        CodecEncoding.getCanonicalCodec(78), // (3,16,1,1)
+        CodecEncoding.getCanonicalCodec(80), // (3,32,1,1)
+        CodecEncoding.getCanonicalCodec(82), // (3,64,1,1)
+        CodecEncoding.getCanonicalCodec(84), // (3,128,1,1)
+        CodecEncoding.getCanonicalCodec(86), // (3,192,1,1)
+        CodecEncoding.getCanonicalCodec(88), // (3,224,1,1)
+        CodecEncoding.getCanonicalCodec(90), // (3,240,1,1)
+        CodecEncoding.getCanonicalCodec(92) // (3,248,1,1)
+    };
+
+    public static BHSDCodec[] deltaSignedCodecs5 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(99), // (4,8,1,1)
+        CodecEncoding.getCanonicalCodec(101), // (4,16,1,1)
+        CodecEncoding.getCanonicalCodec(103), // (4,32,1,1)
+        CodecEncoding.getCanonicalCodec(105), // (4,64,1,1)
+        CodecEncoding.getCanonicalCodec(107), // (4,128,1,1)
+        CodecEncoding.getCanonicalCodec(109), // (4,192,1,1)
+        CodecEncoding.getCanonicalCodec(111), // (4,224,1,1)
+        CodecEncoding.getCanonicalCodec(113), // (4,240,1,1)
+        CodecEncoding.getCanonicalCodec(115) // (4,248,1,1)
+    };
+
+    public static BHSDCodec[] deltaDoubleSignedCodecs1 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(34), // (5,4,2,1)
+        CodecEncoding.getCanonicalCodec(37), // (5,16,2,1)
+        CodecEncoding.getCanonicalCodec(40), // (5,32,2,1)
+        CodecEncoding.getCanonicalCodec(43), // (5,64,2,1)
+        CodecEncoding.getCanonicalCodec(46) // (5,128,2,1)
+    };
+
+    // Families of codecs for bands containing positive and negative values that
+    // do not correlate well (i.e. delta encoding is not used)
+
+    public static BHSDCodec[] nonDeltaSignedCodecs1 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(2), // (1,256,1)
+        CodecEncoding.getCanonicalCodec(6), // (2,256,1)
+        CodecEncoding.getCanonicalCodec(10), // (3,256,1)
+        CodecEncoding.getCanonicalCodec(14) // (4,256,1)
+    };
+
+    public static BHSDCodec[] nonDeltaSignedCodecs2 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(18), // (5,4,1)
+        CodecEncoding.getCanonicalCodec(21), // (5,16,1)
+        CodecEncoding.getCanonicalCodec(24), // (5,32,1)
+        CodecEncoding.getCanonicalCodec(27), // (5,64,1)
+        CodecEncoding.getCanonicalCodec(30) // (5,128,1)
+    };
+
+    public static BHSDCodec[] nonDeltaDoubleSignedCodecs1 = new BHSDCodec[] {
+        CodecEncoding.getCanonicalCodec(19), // (5,4,2)
+        CodecEncoding.getCanonicalCodec(22), // (5,16,2)
+        CodecEncoding.getCanonicalCodec(25), // (5,32,2)
+        CodecEncoding.getCanonicalCodec(28), // (5,64,2)
+        CodecEncoding.getCanonicalCodec(31) // (5,128,2)
+    };
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/ClassBands.java b/src/main/java/org/apache/harmony/pack200/ClassBands.java
new file mode 100644
index 0000000..094a90f
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/ClassBands.java
@@ -0,0 +1,1616 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
+import org.apache.harmony.pack200.IcBands.IcTuple;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Class bands (corresponds to the <code>class_bands</code> set of bands in the
+ * pack200 specification)
+ */
+public class ClassBands extends BandSet {
+
+    private final CpBands cpBands;
+    private final AttributeDefinitionBands attrBands;
+
+    private final CPClass[] class_this;
+    private final CPClass[] class_super;
+    private final CPClass[][] class_interface;
+    private final int[] class_interface_count;
+
+    private final int[] major_versions;
+
+    private final long[] class_flags;
+    private int[] class_attr_calls;
+    private final List classSourceFile = new ArrayList();
+    private final List classEnclosingMethodClass = new ArrayList();
+    private final List classEnclosingMethodDesc = new ArrayList();
+    private final List classSignature = new ArrayList();
+
+    private final IntList classFileVersionMinor = new IntList();
+    private final IntList classFileVersionMajor = new IntList();
+
+    private final int[] class_field_count;
+    private final CPNameAndType[][] field_descr;
+    private final long[][] field_flags;
+    private int[] field_attr_calls;
+    private final List fieldConstantValueKQ = new ArrayList();
+    private final List fieldSignature = new ArrayList();
+
+    private final int[] class_method_count;
+    private final CPNameAndType[][] method_descr;
+    private final long[][] method_flags;
+    private int[] method_attr_calls;
+    private final List methodSignature = new ArrayList();
+    private final IntList methodExceptionNumber = new IntList();
+    private final List methodExceptionClasses = new ArrayList();
+
+    private int[] codeHeaders;
+    private final IntList codeMaxStack = new IntList();
+    private final IntList codeMaxLocals = new IntList();
+    private final IntList codeHandlerCount = new IntList();
+    private final List codeHandlerStartP = new ArrayList();
+    private final List codeHandlerEndPO = new ArrayList();
+    private final List codeHandlerCatchPO = new ArrayList();
+    private final List codeHandlerClass = new ArrayList();
+    private final List codeFlags = new ArrayList();
+    private int[] code_attr_calls;
+    private final IntList codeLineNumberTableN = new IntList();
+    private final List codeLineNumberTableBciP = new ArrayList();
+    private final IntList codeLineNumberTableLine = new IntList();
+    private final IntList codeLocalVariableTableN = new IntList();
+    private final List codeLocalVariableTableBciP = new ArrayList();
+    private final List codeLocalVariableTableSpanO = new ArrayList();
+    private final List codeLocalVariableTableNameRU = new ArrayList();
+    private final List codeLocalVariableTableTypeRS = new ArrayList();
+    private final IntList codeLocalVariableTableSlot = new IntList();
+    private final IntList codeLocalVariableTypeTableN = new IntList();
+    private final List codeLocalVariableTypeTableBciP = new ArrayList();
+    private final List codeLocalVariableTypeTableSpanO = new ArrayList();
+    private final List codeLocalVariableTypeTableNameRU = new ArrayList();
+    private final List codeLocalVariableTypeTableTypeRS = new ArrayList();
+    private final IntList codeLocalVariableTypeTableSlot = new IntList();
+
+    private final MetadataBandGroup class_RVA_bands;
+    private final MetadataBandGroup class_RIA_bands;
+    private final MetadataBandGroup field_RVA_bands;
+    private final MetadataBandGroup field_RIA_bands;
+    private final MetadataBandGroup method_RVA_bands;
+    private final MetadataBandGroup method_RIA_bands;
+    private final MetadataBandGroup method_RVPA_bands;
+    private final MetadataBandGroup method_RIPA_bands;
+    private final MetadataBandGroup method_AD_bands;
+
+    private final List classAttributeBands = new ArrayList();
+    private final List methodAttributeBands = new ArrayList();
+    private final List fieldAttributeBands = new ArrayList();
+    private final List codeAttributeBands = new ArrayList();
+
+    private final List tempFieldFlags = new ArrayList();
+    private final List tempFieldDesc = new ArrayList();
+    private final List tempMethodFlags = new ArrayList();
+    private final List tempMethodDesc = new ArrayList();
+    private TempParamAnnotation tempMethodRVPA;
+    private TempParamAnnotation tempMethodRIPA;
+
+    private boolean anySyntheticClasses = false;
+    private boolean anySyntheticFields = false;
+    private boolean anySyntheticMethods = false;
+    private final Segment segment;
+
+    private final Map classReferencesInnerClass = new HashMap();
+    private final boolean stripDebug;
+
+    private int index = 0;
+
+    private int numMethodArgs = 0;
+    private int[] class_InnerClasses_N;
+    private CPClass[] class_InnerClasses_RC;
+    private int[] class_InnerClasses_F;
+    private List classInnerClassesOuterRCN;
+    private List classInnerClassesNameRUN;
+
+    public ClassBands(Segment segment, int numClasses, int effort, boolean stripDebug) throws IOException {
+        super(effort, segment.getSegmentHeader());
+        this.stripDebug = stripDebug;
+        this.segment = segment;
+        this.cpBands = segment.getCpBands();
+        this.attrBands = segment.getAttrBands();
+        class_this = new CPClass[numClasses];
+        class_super = new CPClass[numClasses];
+        class_interface_count = new int[numClasses];
+        class_interface = new CPClass[numClasses][];
+        class_field_count = new int[numClasses];
+        class_method_count = new int[numClasses];
+        field_descr = new CPNameAndType[numClasses][];
+        field_flags = new long[numClasses][];
+        method_descr = new CPNameAndType[numClasses][];
+        method_flags = new long[numClasses][];
+        for (int i = 0; i < numClasses; i++) {
+            field_flags[i] = new long[0];
+            method_flags[i] = new long[0];
+        }
+        // minor_versions = new int[numClasses];
+        major_versions = new int[numClasses];
+        class_flags = new long[numClasses];
+
+        class_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
+        class_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_CLASS, cpBands, segmentHeader, effort);
+        field_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
+        field_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_FIELD, cpBands, segmentHeader, effort);
+        method_RVA_bands = new MetadataBandGroup("RVA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+        method_RIA_bands = new MetadataBandGroup("RIA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+        method_RVPA_bands = new MetadataBandGroup("RVPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+        method_RIPA_bands = new MetadataBandGroup("RIPA", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+        method_AD_bands = new MetadataBandGroup("AD", MetadataBandGroup.CONTEXT_METHOD, cpBands, segmentHeader, effort);
+
+        createNewAttributeBands();
+    }
+
+    private void createNewAttributeBands() throws IOException {
+        List classAttributeLayouts = attrBands.getClassAttributeLayouts();
+        for (Iterator iterator = classAttributeLayouts.iterator(); iterator.hasNext();) {
+            AttributeDefinition def = (AttributeDefinition) iterator.next();
+            classAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+        }
+        List methodAttributeLayouts = attrBands.getMethodAttributeLayouts();
+        for (Iterator iterator = methodAttributeLayouts.iterator(); iterator.hasNext();) {
+            AttributeDefinition def = (AttributeDefinition) iterator.next();
+            methodAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+        }
+        List fieldAttributeLayouts = attrBands.getFieldAttributeLayouts();
+        for (Iterator iterator = fieldAttributeLayouts.iterator(); iterator.hasNext();) {
+            AttributeDefinition def = (AttributeDefinition) iterator.next();
+            fieldAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+        }
+        List codeAttributeLayouts = attrBands.getCodeAttributeLayouts();
+        for (Iterator iterator = codeAttributeLayouts.iterator(); iterator.hasNext();) {
+            AttributeDefinition def = (AttributeDefinition) iterator.next();
+            codeAttributeBands.add(new NewAttributeBands(effort, cpBands, segment.getSegmentHeader(), def));
+        }
+    }
+
+    public void addClass(int major, int flags, String className,
+            String signature, String superName, String[] interfaces) {
+        class_this[index] = cpBands.getCPClass(className);
+        class_super[index] = cpBands.getCPClass(superName);
+        class_interface_count[index] = interfaces.length;
+        class_interface[index] = new CPClass[interfaces.length];
+        for (int i = 0; i < interfaces.length; i++) {
+            class_interface[index][i] = cpBands.getCPClass(interfaces[i]);
+        }
+        major_versions[index] = major;
+        class_flags[index] = flags;
+        if(!anySyntheticClasses && ((flags & (1 << 12)) != 0) && segment.getCurrentClassReader().hasSyntheticAttributes()) {
+            cpBands.addCPUtf8("Synthetic");
+            anySyntheticClasses = true;
+        }
+        if((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
+            flags = flags & ~Opcodes.ACC_DEPRECATED;
+            flags = flags | (1<<20);
+        }
+        if(signature != null) {
+            class_flags[index] |= (1 << 19);
+            classSignature.add(cpBands.getCPSignature(signature));
+        }
+    }
+
+    public void currentClassReferencesInnerClass(CPClass inner) {
+        if(!(index >= class_this.length)) {
+            CPClass currentClass = class_this[index];
+            if(currentClass != null && !currentClass.equals(inner) && !isInnerClassOf(currentClass.toString(), inner)) {
+                Set referencedInnerClasses = (Set)classReferencesInnerClass.get(currentClass);
+                if(referencedInnerClasses == null) {
+                    referencedInnerClasses = new HashSet();
+                    classReferencesInnerClass.put(currentClass, referencedInnerClasses);
+                }
+                referencedInnerClasses.add(inner);
+            }
+        }
+    }
+
+    private boolean isInnerClassOf(String possibleInner, CPClass possibleOuter) {
+        if(isInnerClass(possibleInner)) {
+            String superClassName = possibleInner.substring(0, possibleInner.lastIndexOf('$'));
+            if(superClassName.equals(possibleOuter.toString())) {
+                return true;
+            } else { // do this recursively
+                return isInnerClassOf(superClassName, possibleOuter);
+            }
+        }
+        return false;
+    }
+
+    private boolean isInnerClass(String possibleInner) {
+        return possibleInner.indexOf('$') != -1;
+    }
+
+    public void addField(int flags, String name, String desc, String signature,
+            Object value) {
+        flags = flags & 0xFFFF;
+        tempFieldDesc.add(cpBands.getCPNameAndType(name, desc));
+        if (signature != null) {
+            fieldSignature.add(cpBands.getCPSignature(signature));
+            flags |= (1 << 19);
+        }
+        if((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
+            flags = flags & ~Opcodes.ACC_DEPRECATED;
+            flags = flags | (1<<20);
+        }
+        if (value != null) {
+            fieldConstantValueKQ.add(cpBands.getConstant(value));
+            flags |= (1 << 17);
+        }
+        if(!anySyntheticFields && ((flags & (1 << 12)) != 0) && segment.getCurrentClassReader().hasSyntheticAttributes()) {
+            cpBands.addCPUtf8("Synthetic");
+            anySyntheticFields = true;
+        }
+        tempFieldFlags.add(new Long(flags));
+    }
+
+    /**
+     * All input classes for the segment have now been read in, so this method
+     * is called so that this class can calculate/complete anything it could not
+     * do while classes were being read.
+     */
+    public void finaliseBands() {
+        int defaultMajorVersion = segmentHeader.getDefaultMajorVersion();
+        for (int i = 0; i < class_flags.length; i++) {
+            int major = major_versions[i];
+            if (major != defaultMajorVersion) {
+                class_flags[i] |= 1 << 24;
+                classFileVersionMajor.add(major);
+                classFileVersionMinor.add(0);
+            }
+        }
+        // Calculate code headers
+        codeHeaders = new int[codeHandlerCount.size()];
+        int removed = 0;
+        for (int i = 0; i < codeHeaders.length; i++) {
+            int numHandlers = codeHandlerCount.get(i - removed);
+            int maxLocals = codeMaxLocals.get(i - removed);
+            int maxStack = codeMaxStack.get(i - removed);
+            if (numHandlers == 0) {
+                int header = maxLocals * 12 + maxStack + 1;
+                if (header < 145 && maxStack < 12) {
+                    codeHeaders[i] = header;
+                }
+            } else if (numHandlers == 1) {
+                int header = maxLocals * 8 + maxStack + 145;
+                if (header < 209 && maxStack < 8) {
+                    codeHeaders[i] = header;
+                }
+            } else if (numHandlers == 2) {
+                int header = maxLocals * 7 + maxStack + 209;
+                if (header < 256 && maxStack < 7) {
+                    codeHeaders[i] = header;
+                }
+            }
+            if (codeHeaders[i] != 0) { // Remove the redundant values from
+                                        // codeHandlerCount, codeMaxLocals and
+                                        // codeMaxStack
+                codeHandlerCount.remove(i - removed);
+                codeMaxLocals.remove(i - removed);
+                codeMaxStack.remove(i - removed);
+                removed++;
+            } else if (!segment.getSegmentHeader().have_all_code_flags()) {
+                codeFlags.add(new Long(0));
+            }
+        }
+
+        // Compute any required IcLocals
+        IntList innerClassesN = new IntList();
+        List icLocal = new ArrayList();
+        for (int i = 0; i < class_this.length; i++) {
+            CPClass cpClass = class_this[i];
+            Set referencedInnerClasses = (Set) classReferencesInnerClass.get(cpClass);
+            if(referencedInnerClasses != null) {
+                int innerN = 0;
+                List innerClasses = segment.getIcBands().getInnerClassesForOuter(cpClass.toString());
+                if(innerClasses != null) {
+                    for (Iterator iterator2 = innerClasses.iterator(); iterator2
+                            .hasNext();) {
+                        referencedInnerClasses.remove(((IcTuple)iterator2.next()).C);
+                    }
+                }
+                for (Iterator iterator2 = referencedInnerClasses.iterator(); iterator2
+                        .hasNext();) {
+                    CPClass inner = (CPClass) iterator2.next();
+                    IcTuple icTuple = segment.getIcBands().getIcTuple(inner);
+                    if(icTuple != null && ! icTuple.isAnonymous()) {
+                        // should transmit an icLocal entry
+                        icLocal.add(icTuple);
+                        innerN++;
+                    }
+                }
+                if(innerN != 0) {
+                    innerClassesN.add(innerN);
+                    class_flags[i] |= (1 << 23);
+                }
+            }
+        }
+        class_InnerClasses_N = innerClassesN.toArray();
+        class_InnerClasses_RC = new CPClass[icLocal.size()];
+        class_InnerClasses_F = new int[icLocal.size()];
+        classInnerClassesOuterRCN = new ArrayList();
+        classInnerClassesNameRUN = new ArrayList();
+        for (int i = 0; i < class_InnerClasses_RC.length; i++) {
+            IcTuple icTuple = (IcTuple) icLocal.get(i);
+            class_InnerClasses_RC[i] = (icTuple.C);
+            if(icTuple.C2 == null && icTuple.N == null) {
+                class_InnerClasses_F[i] = 0;
+            } else {
+                if (icTuple.F == 0) {
+                    class_InnerClasses_F[i] = 0x00010000;
+                } else {
+                    class_InnerClasses_F[i] = icTuple.F;
+                }
+                classInnerClassesOuterRCN.add(icTuple.C2);
+                classInnerClassesNameRUN.add(icTuple.N);
+            }
+        }
+        // Calculate any backwards calls from metadata bands
+        IntList classAttrCalls = new IntList();
+        IntList fieldAttrCalls = new IntList();
+        IntList methodAttrCalls = new IntList();
+        IntList codeAttrCalls = new IntList();
+
+        if(class_RVA_bands.hasContent()) {
+            classAttrCalls.add(class_RVA_bands.numBackwardsCalls());
+        }
+        if(class_RIA_bands.hasContent()) {
+            classAttrCalls.add(class_RIA_bands.numBackwardsCalls());
+        }
+        if(field_RVA_bands.hasContent()) {
+            fieldAttrCalls.add(field_RVA_bands.numBackwardsCalls());
+        }
+        if(field_RIA_bands.hasContent()) {
+            fieldAttrCalls.add(field_RIA_bands.numBackwardsCalls());
+        }
+        if(method_RVA_bands.hasContent()) {
+            methodAttrCalls.add(method_RVA_bands.numBackwardsCalls());
+        }
+        if(method_RIA_bands.hasContent()) {
+            methodAttrCalls.add(method_RIA_bands.numBackwardsCalls());
+        }
+        if(method_RVPA_bands.hasContent()) {
+            methodAttrCalls.add(method_RVPA_bands.numBackwardsCalls());
+        }
+        if(method_RIPA_bands.hasContent()) {
+            methodAttrCalls.add(method_RIPA_bands.numBackwardsCalls());
+        }
+        if(method_AD_bands.hasContent()) {
+            methodAttrCalls.add(method_AD_bands.numBackwardsCalls());
+        }
+
+        // Sort non-predefined attribute bands
+        Comparator comparator = new Comparator() {
+            public int compare(Object arg0, Object arg1) {
+                NewAttributeBands bands0 = (NewAttributeBands)arg0;
+                NewAttributeBands bands1 = (NewAttributeBands)arg1;
+                return bands0.getFlagIndex() - bands1.getFlagIndex();
+            }
+        };
+        Collections.sort(classAttributeBands, comparator);
+        Collections.sort(methodAttributeBands, comparator);
+        Collections.sort(fieldAttributeBands, comparator);
+        Collections.sort(codeAttributeBands, comparator);
+
+        for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.isUsedAtLeastOnce()) {
+                int[] backwardsCallCounts = bands.numBackwardsCalls();
+                for (int i = 0; i < backwardsCallCounts.length; i++) {
+                    classAttrCalls.add(backwardsCallCounts[i]);
+                }
+            }
+        }
+        for (Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.isUsedAtLeastOnce()) {
+                int[] backwardsCallCounts = bands.numBackwardsCalls();
+                for (int i = 0; i < backwardsCallCounts.length; i++) {
+                    methodAttrCalls.add(backwardsCallCounts[i]);
+                }
+            }
+        }
+        for (Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.isUsedAtLeastOnce()) {
+                int[] backwardsCallCounts = bands.numBackwardsCalls();
+                for (int i = 0; i < backwardsCallCounts.length; i++) {
+                    fieldAttrCalls.add(backwardsCallCounts[i]);
+                }
+            }
+        }
+        for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.isUsedAtLeastOnce()) {
+                int[] backwardsCallCounts = bands.numBackwardsCalls();
+                for (int i = 0; i < backwardsCallCounts.length; i++) {
+                    codeAttrCalls.add(backwardsCallCounts[i]);
+                }
+            }
+        }
+
+        class_attr_calls = classAttrCalls.toArray();
+        field_attr_calls = fieldAttrCalls.toArray();
+        method_attr_calls = methodAttrCalls.toArray();
+        code_attr_calls = codeAttrCalls.toArray();
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing class bands...");
+
+        byte[] encodedBand = encodeBandInt("class_this", getInts(class_this),
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_this[" + class_this.length + "]");
+
+        encodedBand = encodeBandInt("class_super", getInts(class_super),
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_super[" + class_super.length + "]");
+
+        encodedBand = encodeBandInt("class_interface_count",
+                class_interface_count, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_interface_count["
+                + class_interface_count.length + "]");
+
+        int totalInterfaces = sum(class_interface_count);
+        int[] classInterface = new int[totalInterfaces];
+        int k = 0;
+        for (int i = 0; i < class_interface.length; i++) {
+            if (class_interface[i] != null) {
+                for (int j = 0; j < class_interface[i].length; j++) {
+                    CPClass cpClass = class_interface[i][j];
+                    classInterface[k] = cpClass.getIndex();
+                    k++;
+                }
+            }
+        }
+
+        encodedBand = encodeBandInt("class_interface", classInterface,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_interface[" + classInterface.length + "]");
+
+        encodedBand = encodeBandInt("class_field_count", class_field_count,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_field_count[" + class_field_count.length
+                + "]");
+
+        encodedBand = encodeBandInt("class_method_count", class_method_count,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_method_count[" + class_method_count.length
+                + "]");
+
+        int totalFields = sum(class_field_count);
+        int[] fieldDescr = new int[totalFields];
+        k = 0;
+        for (int i = 0; i < index; i++) {
+            for (int j = 0; j < field_descr[i].length; j++) {
+                CPNameAndType descr = field_descr[i][j];
+                fieldDescr[k] = descr.getIndex();
+                k++;
+            }
+        }
+
+        encodedBand = encodeBandInt("field_descr", fieldDescr, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from field_descr[" + fieldDescr.length + "]");
+
+        writeFieldAttributeBands(out);
+
+        int totalMethods = sum(class_method_count);
+        int[] methodDescr = new int[totalMethods];
+        k = 0;
+        for (int i = 0; i < index; i++) {
+            for (int j = 0; j < method_descr[i].length; j++) {
+                CPNameAndType descr = method_descr[i][j];
+                methodDescr[k] = descr.getIndex();
+                k++;
+            }
+        }
+
+        encodedBand = encodeBandInt("method_descr", methodDescr, Codec.MDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from method_descr[" + methodDescr.length + "]");
+
+        writeMethodAttributeBands(out);
+        writeClassAttributeBands(out);
+        writeCodeBands(out);
+    }
+
+    private int sum(int[] ints) {
+        int sum = 0;
+        for (int i = 0; i < ints.length; i++) {
+            sum += ints[i];
+        }
+        return sum;
+    }
+
+    private void writeFieldAttributeBands(OutputStream out) throws IOException,
+            Pack200Exception {
+        byte[] encodedBand = encodeFlags("field_flags", field_flags,
+                Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader
+                        .have_field_flags_hi());
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from field_flags[" + field_flags.length + "]");
+
+        // *field_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+        // *field_attr_indexes :UNSIGNED5 [SUM(*field_attr_count)]
+        encodedBand = encodeBandInt("field_attr_calls", field_attr_calls,
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from field_attr_calls[" + field_attr_calls.length
+                + "]");
+
+        encodedBand = encodeBandInt("fieldConstantValueKQ",
+                cpEntryListToArray(fieldConstantValueKQ), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from fieldConstantValueKQ["
+                + fieldConstantValueKQ.size() + "]");
+
+        encodedBand = encodeBandInt("fieldSignature",
+                cpEntryListToArray(fieldSignature), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from fieldSignature[" + fieldSignature.size() + "]");
+
+        field_RVA_bands.pack(out);
+        field_RIA_bands.pack(out);
+        for (Iterator iterator = fieldAttributeBands.iterator(); iterator
+                .hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            bands.pack(out);
+        }
+    }
+
+    private void writeMethodAttributeBands(OutputStream out)
+            throws IOException, Pack200Exception {
+        byte[] encodedBand = encodeFlags("method_flags", method_flags,
+                Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader
+                        .have_method_flags_hi());
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from method_flags[" + method_flags.length + "]");
+
+        // *method_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+        // *method_attr_indexes :UNSIGNED5 [SUM(*method_attr_count)]
+        encodedBand = encodeBandInt("method_attr_calls", method_attr_calls,
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from method_attr_calls[" + method_attr_calls.length
+                + "]");
+
+        encodedBand = encodeBandInt("methodExceptionNumber",
+                methodExceptionNumber.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from methodExceptionNumber["
+                + methodExceptionNumber.size() + "]");
+
+        encodedBand = encodeBandInt("methodExceptionClasses",
+                cpEntryListToArray(methodExceptionClasses), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from methodExceptionClasses["
+                + methodExceptionClasses.size() + "]");
+
+        encodedBand = encodeBandInt("methodSignature",
+                cpEntryListToArray(methodSignature), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils
+                .log("Wrote " + encodedBand.length
+                        + " bytes from methodSignature["
+                        + methodSignature.size() + "]");
+
+        method_RVA_bands.pack(out);
+        method_RIA_bands.pack(out);
+        method_RVPA_bands.pack(out);
+        method_RIPA_bands.pack(out);
+        method_AD_bands.pack(out);
+        for (Iterator iterator = methodAttributeBands.iterator(); iterator
+                .hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            bands.pack(out);
+        }
+    }
+
+    private void writeClassAttributeBands(OutputStream out) throws IOException,
+            Pack200Exception {
+        byte[] encodedBand = encodeFlags("class_flags", class_flags,
+                Codec.UNSIGNED5, Codec.UNSIGNED5, segmentHeader
+                        .have_class_flags_hi());
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_flags[" + class_flags.length + "]");
+
+        // These bands are not needed, but could be used to reduce the size of
+        // the archive if there are enough different non-standard attributes
+        // defined that segmentHeader.have_class_flags_hi() is true. The same
+        // applies to method_attr_count, field_attr_count, code_attr_count etc.
+
+        // *class_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+        // *class_attr_indexes :UNSIGNED5 [SUM(*class_attr_count)]
+
+        encodedBand = encodeBandInt("class_attr_calls", class_attr_calls,
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_attr_calls[" + class_attr_calls.length
+                + "]");
+
+        encodedBand = encodeBandInt("classSourceFile",
+                cpEntryOrNullListToArray(classSourceFile), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils
+                .log("Wrote " + encodedBand.length
+                        + " bytes from classSourceFile["
+                        + classSourceFile.size() + "]");
+
+        encodedBand = encodeBandInt("class_enclosing_method_RC",
+                cpEntryListToArray(classEnclosingMethodClass), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_enclosing_method_RC["
+                + classEnclosingMethodClass.size() + "]");
+
+        encodedBand = encodeBandInt("class_EnclosingMethod_RDN",
+                cpEntryOrNullListToArray(classEnclosingMethodDesc),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_EnclosingMethod_RDN["
+                + classEnclosingMethodDesc.size() + "]");
+
+        encodedBand = encodeBandInt("class_Signature_RS",
+                cpEntryListToArray(classSignature), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_Signature_RS[" + classSignature.size()
+                + "]");
+
+        class_RVA_bands.pack(out);
+        class_RIA_bands.pack(out);
+
+        encodedBand = encodeBandInt("class_InnerClasses_N",
+                class_InnerClasses_N, Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_InnerClasses_N["
+                + class_InnerClasses_N.length + "]");
+
+        encodedBand = encodeBandInt("class_InnerClasses_RC",
+                getInts(class_InnerClasses_RC), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_InnerClasses_RC["
+                + class_InnerClasses_RC.length + "]");
+
+        encodedBand = encodeBandInt("class_InnerClasses_F",
+                class_InnerClasses_F, Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_InnerClasses_F["
+                + class_InnerClasses_F.length + "]");
+
+        encodedBand = encodeBandInt("class_InnerClasses_outer_RCN",
+                cpEntryOrNullListToArray(classInnerClassesOuterRCN),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_InnerClasses_outer_RCN["
+                + classInnerClassesOuterRCN.size() + "]");
+
+        encodedBand = encodeBandInt("class_InnerClasses_name_RUN",
+                cpEntryOrNullListToArray(classInnerClassesNameRUN),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from class_InnerClasses_name_RUN["
+                + classInnerClassesNameRUN.size() + "]");
+
+        encodedBand = encodeBandInt("classFileVersionMinor",
+                classFileVersionMinor.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from classFileVersionMinor["
+                + classFileVersionMinor.size() + "]");
+
+        encodedBand = encodeBandInt("classFileVersionMajor",
+                classFileVersionMajor.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from classFileVersionMajor["
+                + classFileVersionMajor.size() + "]");
+
+        for (Iterator iterator = classAttributeBands.iterator(); iterator
+                .hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            bands.pack(out);
+        }
+    }
+
+    private int[] getInts(CPClass[] cpClasses) {
+        int[] ints = new int[cpClasses.length];
+        for (int i = 0; i < ints.length; i++) {
+            if(cpClasses[i] != null) {
+                ints[i] = cpClasses[i].getIndex();
+            }
+        }
+        return ints;
+    }
+
+    private void writeCodeBands(OutputStream out) throws IOException,
+            Pack200Exception {
+        byte[] encodedBand = encodeBandInt("codeHeaders", codeHeaders,
+                Codec.BYTE1);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeHeaders[" + codeHeaders.length + "]");
+
+        encodedBand = encodeBandInt("codeMaxStack", codeMaxStack.toArray(),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeMaxStack[" + codeMaxStack.size() + "]");
+
+        encodedBand = encodeBandInt("codeMaxLocals", codeMaxLocals.toArray(),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeMaxLocals[" + codeMaxLocals.size() + "]");
+
+        encodedBand = encodeBandInt("codeHandlerCount", codeHandlerCount
+                .toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeHandlerCount[" + codeHandlerCount.size()
+                + "]");
+
+        encodedBand = encodeBandInt("codeHandlerStartP",
+                integerListToArray(codeHandlerStartP), Codec.BCI5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeHandlerStartP[" + codeHandlerStartP.size()
+                + "]");
+
+        encodedBand = encodeBandInt("codeHandlerEndPO",
+                integerListToArray(codeHandlerEndPO), Codec.BRANCH5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeHandlerEndPO[" + codeHandlerEndPO.size()
+                + "]");
+
+        encodedBand = encodeBandInt("codeHandlerCatchPO",
+                integerListToArray(codeHandlerCatchPO), Codec.BRANCH5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeHandlerCatchPO[" + codeHandlerCatchPO.size()
+                + "]");
+
+        encodedBand = encodeBandInt("codeHandlerClass",
+                cpEntryOrNullListToArray(codeHandlerClass), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeHandlerClass[" + codeHandlerClass.size()
+                + "]");
+
+        writeCodeAttributeBands(out);
+    }
+
+    private void writeCodeAttributeBands(OutputStream out) throws IOException,
+            Pack200Exception {
+        byte[] encodedBand = encodeFlags("codeFlags",
+                longListToArray(codeFlags), Codec.UNSIGNED5, Codec.UNSIGNED5,
+                segmentHeader.have_code_flags_hi());
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from codeFlags[" + codeFlags.size() + "]");
+
+        // *code_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
+        // *code_attr_indexes :UNSIGNED5 [SUM(*code_attr_count)]
+        encodedBand = encodeBandInt("code_attr_calls", code_attr_calls,
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils
+                .log("Wrote " + encodedBand.length
+                        + " bytes from code_attr_calls["
+                        + code_attr_calls.length + "]");
+
+        encodedBand = encodeBandInt("code_LineNumberTable_N",
+                codeLineNumberTableN.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LineNumberTable_N["
+                + codeLineNumberTableN.size() + "]");
+
+        encodedBand = encodeBandInt("code_LineNumberTable_bci_P",
+                integerListToArray(codeLineNumberTableBciP), Codec.BCI5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LineNumberTable_bci_P["
+                + codeLineNumberTableBciP.size() + "]");
+
+        encodedBand = encodeBandInt("code_LineNumberTable_line",
+                codeLineNumberTableLine.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LineNumberTable_line["
+                + codeLineNumberTableLine.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTable_N",
+                codeLocalVariableTableN.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTable_N["
+                + codeLocalVariableTableN.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTable_bci_P",
+                integerListToArray(codeLocalVariableTableBciP), Codec.BCI5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTable_bci_P["
+                + codeLocalVariableTableBciP.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTable_span_O",
+                integerListToArray(codeLocalVariableTableSpanO), Codec.BRANCH5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTable_span_O["
+                + codeLocalVariableTableSpanO.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTable_name_RU",
+                cpEntryListToArray(codeLocalVariableTableNameRU),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTable_name_RU["
+                + codeLocalVariableTableNameRU.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTable_type_RS",
+                cpEntryListToArray(codeLocalVariableTableTypeRS),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTable_type_RS["
+                + codeLocalVariableTableTypeRS.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTable_slot",
+                codeLocalVariableTableSlot.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTable_slot["
+                + codeLocalVariableTableSlot.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTypeTable_N",
+                codeLocalVariableTypeTableN.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTypeTable_N["
+                + codeLocalVariableTypeTableN.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTypeTable_bci_P",
+                integerListToArray(codeLocalVariableTypeTableBciP), Codec.BCI5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTypeTable_bci_P["
+                + codeLocalVariableTypeTableBciP.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTypeTable_span_O",
+                integerListToArray(codeLocalVariableTypeTableSpanO), Codec.BRANCH5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTypeTable_span_O["
+                + codeLocalVariableTypeTableSpanO.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTypeTable_name_RU",
+                cpEntryListToArray(codeLocalVariableTypeTableNameRU),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTypeTable_name_RU["
+                + codeLocalVariableTypeTableNameRU.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTypeTable_type_RS",
+                cpEntryListToArray(codeLocalVariableTypeTableTypeRS),
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTypeTable_type_RS["
+                + codeLocalVariableTypeTableTypeRS.size() + "]");
+
+        encodedBand = encodeBandInt("code_LocalVariableTypeTable_slot",
+                codeLocalVariableTypeTableSlot.toArray(), Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from code_LocalVariableTypeTable_slot["
+                + codeLocalVariableTypeTableSlot.size() + "]");
+
+        for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            bands.pack(out);
+        }
+    }
+
+    public void addMethod(int flags, String name, String desc,
+            String signature, String[] exceptions) {
+        CPNameAndType nt = cpBands.getCPNameAndType(name, desc);
+        tempMethodDesc.add(nt);
+        if (signature != null) {
+            methodSignature.add(cpBands.getCPSignature(signature));
+            flags |= (1 << 19);
+        }
+        if (exceptions != null) {
+            methodExceptionNumber.add(exceptions.length);
+            for (int i = 0; i < exceptions.length; i++) {
+                methodExceptionClasses.add(cpBands.getCPClass(exceptions[i]));
+            }
+            flags |= (1 << 18);
+        }
+        if((flags & Opcodes.ACC_DEPRECATED) != 0) { // ASM uses (1<<17) flag for deprecated
+            flags = flags & ~Opcodes.ACC_DEPRECATED;
+            flags = flags | (1<<20);
+        }
+        tempMethodFlags.add(new Long(flags));
+        numMethodArgs = countArgs(desc);
+        if(!anySyntheticMethods && ((flags & (1 << 12)) != 0) && segment.getCurrentClassReader().hasSyntheticAttributes()) {
+            cpBands.addCPUtf8("Synthetic");
+            anySyntheticMethods = true;
+        }
+    }
+
+    public void endOfMethod() {
+        if (tempMethodRVPA != null) {
+            method_RVPA_bands.addParameterAnnotation(tempMethodRVPA.numParams,
+                    tempMethodRVPA.annoN, tempMethodRVPA.pairN,
+                    tempMethodRVPA.typeRS, tempMethodRVPA.nameRU,
+                    tempMethodRVPA.t, tempMethodRVPA.values,
+                    tempMethodRVPA.caseArrayN, tempMethodRVPA.nestTypeRS,
+                    tempMethodRVPA.nestNameRU, tempMethodRVPA.nestPairN);
+            tempMethodRVPA = null;
+        }
+        if (tempMethodRIPA != null) {
+            method_RIPA_bands.addParameterAnnotation(tempMethodRIPA.numParams,
+                    tempMethodRIPA.annoN, tempMethodRIPA.pairN,
+                    tempMethodRIPA.typeRS, tempMethodRIPA.nameRU,
+                    tempMethodRIPA.t, tempMethodRIPA.values,
+                    tempMethodRIPA.caseArrayN, tempMethodRIPA.nestTypeRS,
+                    tempMethodRIPA.nestNameRU, tempMethodRIPA.nestPairN);
+            tempMethodRIPA = null;
+        }
+        if(codeFlags.size() > 0) {
+            long latestCodeFlag = ((Long)codeFlags.get(codeFlags.size() - 1)).longValue();
+            int latestLocalVariableTableN = codeLocalVariableTableN.get(codeLocalVariableTableN.size() - 1);
+            if(latestCodeFlag == (1 << 2) && latestLocalVariableTableN == 0) {
+                codeLocalVariableTableN.remove(codeLocalVariableTableN.size() - 1);
+                codeFlags.remove(codeFlags.size() - 1);
+                codeFlags.add(new Integer(0));
+            }
+        }
+    }
+
+    protected static int countArgs(String descriptor) {
+        int bra = descriptor.indexOf('(');
+        int ket = descriptor.indexOf(')');
+        if (bra == -1 || ket == -1 || ket < bra)
+            throw new IllegalArgumentException("No arguments");
+
+        boolean inType = false;
+        boolean consumingNextType = false;
+        int count = 0;
+        for (int i = bra + 1; i < ket; i++) {
+            char charAt = descriptor.charAt(i);
+            if (inType && charAt == ';') {
+                inType = false;
+                consumingNextType = false;
+            } else if (!inType && charAt == 'L') {
+                inType = true;
+                count++;
+            } else if (charAt == '[') {
+                consumingNextType = true;
+            } else if (inType) {
+                // NOP
+            } else {
+                if (consumingNextType) {
+                    count++;
+                    consumingNextType = false;
+                } else {
+                    if (charAt == 'D' || charAt == 'J') {
+                        count += 2;
+                    } else {
+                        count++;
+                    }
+                }
+            }
+        }
+        return count;
+    }
+
+    public void endOfClass() { // All the data for the current class has been
+                                // read
+        int numFields = tempFieldDesc.size();
+        class_field_count[index] = numFields;
+        field_descr[index] = new CPNameAndType[numFields];
+        field_flags[index] = new long[numFields];
+        for (int i = 0; i < numFields; i++) {
+            field_descr[index][i] = (CPNameAndType) tempFieldDesc.get(i);
+            field_flags[index][i] = ((Long) tempFieldFlags.get(i)).longValue();
+        }
+        int numMethods = tempMethodDesc.size();
+        class_method_count[index] = numMethods;
+        method_descr[index] = new CPNameAndType[numMethods];
+        method_flags[index] = new long[numMethods];
+        for (int i = 0; i < numMethods; i++) {
+            method_descr[index][i] = (CPNameAndType) tempMethodDesc.get(i);
+            method_flags[index][i] = ((Long) tempMethodFlags.get(i))
+                    .longValue();
+        }
+        tempFieldDesc.clear();
+        tempFieldFlags.clear();
+        tempMethodDesc.clear();
+        tempMethodFlags.clear();
+        index++;
+    }
+
+    public void addSourceFile(String source) {
+        String implicitSourceFileName = class_this[index].toString();
+        if(implicitSourceFileName.indexOf('$') != -1) {
+            implicitSourceFileName = implicitSourceFileName.substring(0, implicitSourceFileName.indexOf('$'));
+        }
+        implicitSourceFileName = implicitSourceFileName
+                .substring(implicitSourceFileName.lastIndexOf('/') + 1)
+                + ".java";
+        if (source.equals(implicitSourceFileName)) {
+            classSourceFile.add(null);
+        } else {
+            classSourceFile.add(cpBands.getCPUtf8(source));
+        }
+        class_flags[index] |= (1 << 17);
+    }
+
+    public void addEnclosingMethod(String owner, String name, String desc) {
+        class_flags[index] |= (1 << 18);
+        classEnclosingMethodClass.add(cpBands.getCPClass(owner));
+        classEnclosingMethodDesc.add(name == null ? null : cpBands
+                .getCPNameAndType(name, desc));
+    }
+
+    public void addClassAttribute(NewAttribute attribute) {
+        // TODO: backwards calls
+        String attributeName = attribute.type;
+        for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.getAttributeName().equals(attributeName)) {
+                bands.addAttribute(attribute);
+                int flagIndex = bands.getFlagIndex();
+                class_flags[index] |= (1 << flagIndex);
+                return;
+            }
+        }
+        throw new RuntimeException("No suitable definition for " + attributeName);
+    }
+
+    public void addFieldAttribute(NewAttribute attribute) {
+        String attributeName = attribute.type;
+        for (Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.getAttributeName().equals(attributeName)) {
+                bands.addAttribute(attribute);
+                int flagIndex = bands.getFlagIndex();
+                Long flags = (Long)tempFieldFlags.remove(tempFieldFlags.size() - 1);
+                tempFieldFlags.add(new Long(flags.longValue() | (1 << flagIndex)));
+                return;
+            }
+        }
+        throw new RuntimeException("No suitable definition for " + attributeName);
+    }
+
+    public void addMethodAttribute(NewAttribute attribute) {
+        String attributeName = attribute.type;
+        for (Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.getAttributeName().equals(attributeName)) {
+                bands.addAttribute(attribute);
+                int flagIndex = bands.getFlagIndex();
+                Long flags = (Long)tempMethodFlags.remove(tempMethodFlags.size() - 1);
+                tempMethodFlags.add(new Long(flags.longValue() | (1 << flagIndex)));
+                return;
+            }
+        }
+        throw new RuntimeException("No suitable definition for " + attributeName);
+    }
+
+    public void addCodeAttribute(NewAttribute attribute) {
+        String attributeName = attribute.type;
+        for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            if(bands.getAttributeName().equals(attributeName)) {
+                bands.addAttribute(attribute);
+                int flagIndex = bands.getFlagIndex();
+                Long flags = (Long)codeFlags.remove(codeFlags.size() - 1);
+                codeFlags.add(new Long(flags.longValue() | (1 << flagIndex)));
+                return;
+            }
+        }
+        throw new RuntimeException("No suitable definition for " + attributeName);
+    }
+
+    public void addMaxStack(int maxStack, int maxLocals) {
+        Long latestFlag = (Long) tempMethodFlags
+                .remove(tempMethodFlags.size() - 1);
+        Long newFlag = new Long(latestFlag.intValue() | (1 << 17));
+        tempMethodFlags.add(newFlag);
+        codeMaxStack.add(maxStack);
+        if ((newFlag.longValue() & (1 << 3)) == 0) { // not static
+            maxLocals--; // minus 'this' local
+        }
+        maxLocals -= numMethodArgs;
+        codeMaxLocals.add(maxLocals);
+    }
+
+    public void addCode() {
+        codeHandlerCount.add(0);
+        if(!stripDebug) {
+            codeFlags.add(new Long((1 << 2)));
+            codeLocalVariableTableN.add(0);
+        }
+    }
+
+    public void addHandler(Label start, Label end, Label handler, String type) {
+        int handlers = codeHandlerCount.remove(codeHandlerCount
+                .size() - 1);
+        codeHandlerCount.add(handlers + 1);
+        codeHandlerStartP.add(start);
+        codeHandlerEndPO.add(end);
+        codeHandlerCatchPO.add(handler);
+        codeHandlerClass.add(type == null ? null : cpBands.getCPClass(type));
+    }
+
+    public void addLineNumber(int line, Label start) {
+        Long latestCodeFlag = (Long) codeFlags.get(codeFlags.size() - 1);
+        if ((latestCodeFlag.intValue() & (1 << 1)) == 0) {
+            codeFlags.remove(codeFlags.size() - 1);
+            codeFlags.add(new Long(latestCodeFlag.intValue() | (1 << 1)));
+            codeLineNumberTableN.add(1);
+        } else {
+            codeLineNumberTableN
+                    .increment(codeLineNumberTableN.size() - 1);
+        }
+        codeLineNumberTableLine.add(line);
+        codeLineNumberTableBciP.add(start);
+    }
+
+    public void addLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int indx) {
+        if (signature != null) { // LocalVariableTypeTable attribute
+            Long latestCodeFlag = (Long) codeFlags.get(codeFlags.size() - 1);
+            if ((latestCodeFlag.intValue() & (1 << 3)) == 0) {
+                codeFlags.remove(codeFlags.size() - 1);
+                codeFlags.add(new Long(latestCodeFlag.intValue() | (1 << 3)));
+                codeLocalVariableTypeTableN.add(1);
+            } else {
+                codeLocalVariableTypeTableN
+                        .increment(codeLocalVariableTypeTableN.size() - 1);
+            }
+            codeLocalVariableTypeTableBciP.add(start);
+            codeLocalVariableTypeTableSpanO.add(end);
+            codeLocalVariableTypeTableNameRU.add(cpBands.getCPUtf8(name));
+            codeLocalVariableTypeTableTypeRS.add(cpBands
+                    .getCPSignature(signature));
+            codeLocalVariableTypeTableSlot.add(indx);
+        }
+        // LocalVariableTable attribute
+        codeLocalVariableTableN
+                .increment(codeLocalVariableTableN.size() - 1);
+        codeLocalVariableTableBciP.add(start);
+        codeLocalVariableTableSpanO.add(end);
+        codeLocalVariableTableNameRU.add(cpBands.getCPUtf8(name));
+        codeLocalVariableTableTypeRS.add(cpBands.getCPSignature(desc));
+        codeLocalVariableTableSlot.add(indx);
+    }
+
+    public void doBciRenumbering(IntList bciRenumbering, Map labelsToOffsets) {
+        renumberBci(codeLineNumberTableBciP, bciRenumbering, labelsToOffsets);
+        renumberBci(codeLocalVariableTableBciP, bciRenumbering, labelsToOffsets);
+        renumberOffsetBci(codeLocalVariableTableBciP,
+                codeLocalVariableTableSpanO, bciRenumbering, labelsToOffsets);
+        renumberBci(codeLocalVariableTypeTableBciP, bciRenumbering,
+                labelsToOffsets);
+        renumberOffsetBci(codeLocalVariableTypeTableBciP,
+                codeLocalVariableTypeTableSpanO, bciRenumbering,
+                labelsToOffsets);
+        renumberBci(codeHandlerStartP, bciRenumbering, labelsToOffsets);
+        renumberOffsetBci(codeHandlerStartP, codeHandlerEndPO,
+                bciRenumbering, labelsToOffsets);
+        renumberDoubleOffsetBci(codeHandlerStartP, codeHandlerEndPO, codeHandlerCatchPO,
+                bciRenumbering, labelsToOffsets);
+
+        for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+        }
+        for (Iterator iterator = methodAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+        }
+        for (Iterator iterator = fieldAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+        }
+        for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands newAttributeBandSet = (NewAttributeBands) iterator.next();
+            newAttributeBandSet.renumberBci(bciRenumbering, labelsToOffsets);
+        }
+    }
+
+    private void renumberBci(List list, IntList bciRenumbering, Map labelsToOffsets) {
+        for (int i = list.size() - 1; i >= 0; i--) {
+            Object label = list.get(i);
+            if (label instanceof Integer) {
+                break;
+            } else if (label instanceof Label) {
+                list.remove(i);
+                Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+                list.add(i, new Integer(bciRenumbering.get(bytecodeIndex.intValue())));
+            }
+        }
+    }
+
+    private void renumberOffsetBci(List relative, List list,
+            IntList bciRenumbering, Map labelsToOffsets) {
+        for (int i = list.size() - 1; i >= 0; i--) {
+            Object label = list.get(i);
+            if (label instanceof Integer) {
+                break;
+            } else if (label instanceof Label) {
+                list.remove(i);
+                Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+                Integer renumberedOffset = new Integer(bciRenumbering
+                        .get(bytecodeIndex.intValue())
+                        - ((Integer) relative.get(i)).intValue());
+                list.add(i, renumberedOffset);
+            }
+        }
+    }
+
+    private void renumberDoubleOffsetBci(List relative, List firstOffset, List list,
+            IntList bciRenumbering, Map labelsToOffsets) {
+        // TODO: There's probably a nicer way of doing this...
+        for (int i = list.size() - 1; i >= 0; i--) {
+            Object label = list.get(i);
+            if (label instanceof Integer) {
+                break;
+            } else if (label instanceof Label) {
+                list.remove(i);
+                Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+                Integer renumberedOffset = new Integer(bciRenumbering
+                        .get(bytecodeIndex.intValue())
+                        - ((Integer) relative.get(i)).intValue() - ((Integer) firstOffset.get(i)).intValue());
+                list.add(i, renumberedOffset);
+            }
+        }
+    }
+
+    public boolean isAnySyntheticClasses() {
+        return anySyntheticClasses;
+    }
+
+    public boolean isAnySyntheticFields() {
+        return anySyntheticFields;
+    }
+
+    public boolean isAnySyntheticMethods() {
+        return anySyntheticMethods;
+    }
+
+    public void addParameterAnnotation(int parameter, String desc,
+            boolean visible, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+        if(visible) {
+            if(tempMethodRVPA == null) {
+                tempMethodRVPA = new TempParamAnnotation(numMethodArgs);
+                tempMethodRVPA.addParameterAnnotation(parameter, desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+            }
+            Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+            tempMethodFlags.add(new Long(flag.longValue() | (1<<23)));
+        } else {
+            if(tempMethodRIPA == null) {
+                tempMethodRIPA = new TempParamAnnotation(numMethodArgs);
+                tempMethodRIPA.addParameterAnnotation(parameter, desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+            }
+            Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+            tempMethodFlags.add(new Long(flag.longValue() | (1<<24)));
+        }
+    }
+
+    private static class TempParamAnnotation {
+
+        int numParams;
+        int[] annoN;
+        IntList pairN = new IntList();
+        List typeRS = new ArrayList();
+        List nameRU = new ArrayList();
+        List t = new ArrayList();
+        List values = new ArrayList();
+        List caseArrayN = new ArrayList();
+        List nestTypeRS = new ArrayList();
+        List nestNameRU = new ArrayList();
+        List nestPairN = new ArrayList();
+
+        public TempParamAnnotation (int numParams) {
+            this.numParams = numParams;
+            annoN = new int[numParams];
+        }
+
+        public void addParameterAnnotation(int parameter, String desc,
+                List nameRU, List t, List values, List caseArrayN,
+                List nestTypeRS, List nestNameRU, List nestPairN) {
+            annoN[parameter]++;
+            typeRS.add(desc);
+            pairN.add(nameRU.size());
+            this.nameRU.addAll(nameRU);
+            this.t.addAll(t);
+            this.values.addAll(values);
+            this.caseArrayN.addAll(caseArrayN);
+            this.nestTypeRS.addAll(nestTypeRS);
+            this.nestNameRU.addAll(nestNameRU);
+            this.nestPairN.addAll(nestPairN);
+        }
+    }
+
+    public void addAnnotation(int context, String desc, boolean visible, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+        switch (context) {
+        case MetadataBandGroup.CONTEXT_CLASS:
+            if(visible) {
+                class_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+                if((class_flags[index] & (1<<21)) != 0) {
+                    class_RVA_bands.incrementAnnoN();
+                } else {
+                    class_RVA_bands.newEntryInAnnoN();
+                    class_flags[index] = class_flags[index] | (1<<21);
+                }
+            } else {
+                class_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+                if((class_flags[index] & (1<<22)) != 0) {
+                    class_RIA_bands.incrementAnnoN();
+                } else {
+                    class_RIA_bands.newEntryInAnnoN();
+                    class_flags[index] = class_flags[index] | (1<<22);
+                }
+            }
+            break;
+        case MetadataBandGroup.CONTEXT_FIELD:
+            if(visible) {
+                field_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+                Long flag = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
+                if((flag.intValue() & (1<<21)) != 0) {
+                    field_RVA_bands.incrementAnnoN();
+                } else {
+                    field_RVA_bands.newEntryInAnnoN();
+                }
+                tempFieldFlags.add(new Long(flag.intValue() | (1<<21)));
+            } else {
+                field_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+                Long flag = (Long) tempFieldFlags.remove(tempFieldFlags.size() - 1);
+                if((flag.intValue() & (1<<22)) != 0) {
+                    field_RIA_bands.incrementAnnoN();
+                } else {
+                    field_RIA_bands.newEntryInAnnoN();
+                }
+                tempFieldFlags.add(new Long(flag.intValue() | (1<<22)));
+            }
+            break;
+        case MetadataBandGroup.CONTEXT_METHOD:
+            if(visible) {
+                method_RVA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+                Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+                if((flag.intValue() & (1<<21)) != 0) {
+                    method_RVA_bands.incrementAnnoN();
+                } else {
+                    method_RVA_bands.newEntryInAnnoN();
+                }
+                tempMethodFlags.add(new Long(flag.intValue() | (1<<21)));
+            } else {
+                method_RIA_bands.addAnnotation(desc, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+                Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+                if((flag.intValue() & (1<<22)) != 0) {
+                    method_RIA_bands.incrementAnnoN();
+                } else {
+                    method_RIA_bands.newEntryInAnnoN();
+                }
+                tempMethodFlags.add(new Long(flag.intValue() | (1<<22)));
+            }
+            break;
+        }
+    }
+
+    public void addAnnotationDefault(List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+        method_AD_bands.addAnnotation(null, nameRU, t, values, caseArrayN, nestTypeRS, nestNameRU, nestPairN);
+        Long flag = (Long) tempMethodFlags.remove(tempMethodFlags.size() - 1);
+        tempMethodFlags.add(new Long(flag.longValue() | (1<<25)));
+    }
+
+    /**
+     * Remove all entries for the current class
+     */
+    public void removeCurrentClass() {
+        // Note - this doesn't remove any entries added to the constant pool but
+        // that shouldn't be a problem
+        if ((class_flags[index] & (1 << 17)) != 0) {
+            classSourceFile.remove(classSourceFile.size() - 1);
+        }
+        if ((class_flags[index] & (1 << 18)) != 0) {
+            classEnclosingMethodClass
+                    .remove(classEnclosingMethodClass.size() - 1);
+            classEnclosingMethodDesc
+                    .remove(classEnclosingMethodDesc.size() - 1);
+        }
+        if ((class_flags[index] & (1 << 19)) != 0) {
+            classSignature.remove(classSignature.size() - 1);
+        }
+        if ((class_flags[index] & (1 << 21)) != 0) {
+            class_RVA_bands.removeLatest();
+        }
+        if ((class_flags[index] & (1 << 22)) != 0) {
+            class_RIA_bands.removeLatest();
+        }
+        for (Iterator iterator = tempFieldFlags.iterator(); iterator.hasNext();) {
+            Long flagsL = (Long) iterator.next();
+            long flags = flagsL.longValue();
+            if ((flags & (1 << 19)) != 0) {
+                fieldSignature.remove(fieldSignature.size() - 1);
+            }
+            if ((flags & (1 << 17)) != 0) {
+                fieldConstantValueKQ.remove(fieldConstantValueKQ.size() - 1);
+            }
+            if ((flags & (1 << 21)) != 0) {
+                field_RVA_bands.removeLatest();
+            }
+            if ((flags & (1 << 22)) != 0) {
+                field_RIA_bands.removeLatest();
+            }
+        }
+        for (Iterator iterator = tempMethodFlags.iterator(); iterator.hasNext();) {
+            Long flagsL = (Long) iterator.next();
+            long flags = flagsL.longValue();
+            if ((flags & (1 << 19)) != 0) {
+                methodSignature.remove(methodSignature.size() - 1);
+            }
+            if ((flags & (1 << 18)) != 0) {
+                int exceptions = methodExceptionNumber
+                        .remove(methodExceptionNumber.size() - 1);
+                for (int i = 0; i < exceptions; i++) {
+                    methodExceptionClasses
+                            .remove(methodExceptionClasses.size() - 1);
+                }
+            }
+            if ((flags & (1 << 17)) != 0) { // has code attribute
+                codeMaxLocals.remove(codeMaxLocals.size() - 1);
+                codeMaxStack.remove(codeMaxStack.size() - 1);
+                int handlers = codeHandlerCount
+                        .remove(codeHandlerCount.size() - 1);
+                for (int i = 0; i < handlers; i++) {
+                    int index = codeHandlerStartP.size() - 1;
+                    codeHandlerStartP.remove(index);
+                    codeHandlerEndPO.remove(index);
+                    codeHandlerCatchPO.remove(index);
+                    codeHandlerClass.remove(index);
+                }
+                if (!stripDebug) {
+                    long cdeFlags = ((Long) codeFlags
+                            .remove(codeFlags.size() - 1)).longValue();
+                    int numLocalVariables = codeLocalVariableTableN
+                            .remove(codeLocalVariableTableN.size() - 1);
+                    for (int i = 0; i < numLocalVariables; i++) {
+                        int location = codeLocalVariableTableBciP.size() - 1;
+                        codeLocalVariableTableBciP.remove(location);
+                        codeLocalVariableTableSpanO.remove(location);
+                        codeLocalVariableTableNameRU.remove(location);
+                        codeLocalVariableTableTypeRS.remove(location);
+                        codeLocalVariableTableSlot.remove(location);
+                    }
+                    if ((cdeFlags & (1 << 3)) != 0) {
+                        int numLocalVariablesInTypeTable = codeLocalVariableTypeTableN
+                                .remove(codeLocalVariableTypeTableN.size() - 1);
+                        for (int i = 0; i < numLocalVariablesInTypeTable; i++) {
+                            int location = codeLocalVariableTypeTableBciP
+                                    .size() - 1;
+                            codeLocalVariableTypeTableBciP.remove(location);
+                            codeLocalVariableTypeTableSpanO.remove(location);
+                            codeLocalVariableTypeTableNameRU.remove(location);
+                            codeLocalVariableTypeTableTypeRS.remove(location);
+                            codeLocalVariableTypeTableSlot.remove(location);
+                        }
+                    }
+                    if ((cdeFlags & (1 << 1)) != 0) {
+                        int numLineNumbers = codeLineNumberTableN
+                                .remove(codeLineNumberTableN.size() - 1);
+                        for (int i = 0; i < numLineNumbers; i++) {
+                            int location = codeLineNumberTableBciP.size() - 1;
+                            codeLineNumberTableBciP.remove(location);
+                            codeLineNumberTableLine.remove(location);
+                        }
+                    }
+                }
+            }
+            if ((flags & (1 << 21)) != 0) {
+                method_RVA_bands.removeLatest();
+            }
+            if ((flags & (1 << 22)) != 0) {
+                method_RIA_bands.removeLatest();
+            }
+            if ((flags & (1 << 23)) != 0) {
+                method_RVPA_bands.removeLatest();
+            }
+            if ((flags & (1 << 24)) != 0) {
+                method_RIPA_bands.removeLatest();
+            }
+            if ((flags & (1 << 25)) != 0) {
+                method_AD_bands.removeLatest();
+            }
+        }
+        class_this[index] = null;
+        class_super[index] = null;
+        class_interface_count[index] = 0;
+        class_interface[index] = null;
+        major_versions[index] = 0;
+        class_flags[index] = 0;
+        tempFieldDesc.clear();
+        tempFieldFlags.clear();
+        tempMethodDesc.clear();
+        tempMethodFlags.clear();
+        if(index > 0) {
+            index--;
+        }
+    }
+
+    public int numClassesProcessed() {
+        return index;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Codec.java b/src/main/java/org/apache/harmony/pack200/Codec.java
new file mode 100644
index 0000000..83b80e0
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Codec.java
@@ -0,0 +1,246 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * A Codec allows a sequence of bytes to be decoded into integer values (or vice
+ * versa).
+ *
+ * There are a number of standard Codecs ({@link #UDELTA5}, {@link #UNSIGNED5},
+ * {@link #BYTE1}, {@link #CHAR3}) that are used in the implementation of many
+ * bands; but there are a variety of other ones, and indeed the specification
+ * assumes that other combinations of values can result in more specific and
+ * efficient formats. There are also a sequence of canonical encodings defined
+ * by the Pack200 specification, which allow a Codec to be referred to by
+ * canonical number. {@link CodecEncoding#getCodec(int, InputStream, Codec)})
+ */
+public abstract class Codec {
+
+    /**
+     * BCI5 = (5,4): Used for storing branching information in bytecode.
+     */
+    public static final BHSDCodec BCI5 = new BHSDCodec(5, 4);
+
+    /**
+     * BRANCH5 = (5,4,2): Used for storing branching information in bytecode.
+     */
+    public static final BHSDCodec BRANCH5 = new BHSDCodec(5, 4, 2);
+
+    /**
+     * BYTE1 = (1,256): Used for storing plain bytes.
+     */
+    public static final BHSDCodec BYTE1 = new BHSDCodec(1, 256);
+
+    /**
+     * CHAR3 = (3,128): Used for storing text (UTF-8) strings. NB This isn't
+     * quite the same as UTF-8, but has similar properties; ASCII characters
+     * &lt; 127 are stored in a single byte.
+     */
+    public static final BHSDCodec CHAR3 = new BHSDCodec(3, 128);
+
+    /**
+     * DELTA5 = (5,64,1,1): Used for the majority of numerical codings where
+     * there is a correlated sequence of signed values.
+     */
+    public static final BHSDCodec DELTA5 = new BHSDCodec(5, 64, 1, 1);
+
+    /**
+     * MDELTA5 = (5,64,2,1): Used for the majority of numerical codings where
+     * there is a correlated sequence of signed values, but where most of them
+     * are expected to be non-negative.
+     */
+    public static final BHSDCodec MDELTA5 = new BHSDCodec(5, 64, 2, 1);
+
+    /**
+     * SIGNED5 = (5,64,1): Used for small signed values.
+     */
+    public static final BHSDCodec SIGNED5 = new BHSDCodec(5, 64, 1);
+
+    /**
+     * UDELTA5 = (5,64,0,1): Used for the majority of numerical codings where
+     * there is a correlated sequence of unsigned values.
+     */
+    public static final BHSDCodec UDELTA5 = new BHSDCodec(5, 64, 0, 1);
+
+    /**
+     * UNSIGNED5 = (5,64): Used for small unsigned values.
+     */
+    public static final BHSDCodec UNSIGNED5 = new BHSDCodec(5, 64);
+
+    public int lastBandLength;
+
+    /**
+     * Decode a sequence of bytes from the given input stream, returning the
+     * value as a long. Note that this method can only be applied for non-delta
+     * encodings.
+     *
+     * @param in
+     *            the input stream to read from
+     * @return the value as a long
+     * @throws IOException
+     *             if there is a problem reading from the underlying input
+     *             stream
+     * @throws Pack200Exception
+     *             if the encoding is a delta encoding
+     */
+    public abstract int decode(InputStream in) throws IOException,
+            Pack200Exception;
+
+    /**
+     * Encode a single value into a sequence of bytes.
+     *
+     * @param value
+     *            the value to encode
+     * @param last
+     *            the previous value encoded (for delta encodings)
+     * @return the encoded bytes
+     * @throws Pack200Exception
+     */
+    public abstract byte[] encode(int value, int last)
+            throws Pack200Exception;
+
+    /**
+     * Encode a single value into a sequence of bytes. Note that this method can
+     * only be used for non-delta encodings.
+     *
+     * @param value
+     *            the value to encode
+     * @return the encoded bytes
+     * @throws Pack200Exception
+     */
+    public abstract byte[] encode(int value) throws Pack200Exception;
+
+    /**
+     * Decode a sequence of bytes from the given input stream, returning the
+     * value as a long. If this encoding is a delta encoding (d=1) then the
+     * previous value must be passed in as a parameter. If it is a non-delta
+     * encoding, then it does not matter what value is passed in, so it makes
+     * sense for the value to be passed in by default using code similar to:
+     *
+     * <pre>
+     * long last = 0;
+     * while (condition) {
+     *     last = codec.decode(in, last);
+     *     // do something with last
+     * }
+     * </pre>
+     *
+     * @param in
+     *            the input stream to read from
+     * @param last
+     *            the previous value read, which must be supplied if the codec
+     *            is a delta encoding
+     * @return the value as a long
+     * @throws IOException
+     *             if there is a problem reading from the underlying input
+     *             stream
+     * @throws Pack200Exception
+     *             if there is a problem decoding the value or that the value is
+     *             invalid
+     */
+    public abstract int decode(InputStream in, long last) throws IOException,
+            Pack200Exception;
+
+    /**
+     * Decodes a sequence of <code>n</code> values from <code>in</code>.
+     * This should probably be used in most cases, since some codecs (such as
+     *
+     * @{link PopCodec}) only work when the number of values to be read is
+     *        known.
+     *
+     * @param n
+     *            the number of values to decode
+     * @param in
+     *            the input stream to read from
+     * @return an array of <code>int</code> values corresponding to values
+     *         decoded
+     * @throws IOException
+     *             if there is a problem reading from the underlying input
+     *             stream
+     * @throws Pack200Exception
+     *             if there is a problem decoding the value or that the value is
+     *             invalid
+     */
+    public int[] decodeInts(int n, InputStream in) throws IOException,
+            Pack200Exception {
+        lastBandLength = 0;
+        int result[] = new int[n];
+        int last = 0;
+        for (int i = 0; i < n; i++) {
+            result[i] = last = decode(in, last);
+        }
+        return result;
+    }
+
+    /**
+     * Decodes a sequence of <code>n</code> values from <code>in</code>.
+     *
+     * @param n
+     *            the number of values to decode
+     * @param in
+     *            the input stream to read from
+     * @param firstValue
+     *            the first value in the band if it has already been read
+     * @return an array of <code>int</code> values corresponding to values
+     *         decoded, with firstValue as the first value in the array.
+     * @throws IOException
+     *             if there is a problem reading from the underlying input
+     *             stream
+     * @throws Pack200Exception
+     *             if there is a problem decoding the value or that the value is
+     *             invalid
+     */
+    public int[] decodeInts(int n, InputStream in, int firstValue)
+            throws IOException, Pack200Exception {
+        int result[] = new int[n + 1];
+        result[0] = firstValue;
+        int last = firstValue;
+        for (int i = 1; i < n + 1; i++) {
+            result[i] = last = decode(in, last);
+        }
+        return result;
+    }
+
+    /**
+     * Encode a sequence of integers into a byte array
+     *
+     * @param ints
+     *            the values to encode
+     * @return byte[] encoded bytes
+     * @throws Pack200Exception
+     *             if there is a problem encoding any of the values
+     */
+    public byte[] encode(int[] ints) throws Pack200Exception {
+        int total = 0;
+        byte[][] bytes = new byte[ints.length][];
+        for (int i = 0; i < ints.length; i++) {
+            bytes[i] = encode(ints[i], i > 0 ? ints[i-1] : 0);
+            total += bytes[i].length;
+        }
+        byte[] encoded = new byte[total];
+        int index = 0;
+        for (int i = 0; i < bytes.length; i++) {
+            System.arraycopy(bytes[i], 0, encoded, index, bytes[i].length);
+            index += bytes[i].length;
+        }
+        return encoded;
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CodecEncoding.java b/src/main/java/org/apache/harmony/pack200/CodecEncoding.java
new file mode 100644
index 0000000..c7df8a4
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CodecEncoding.java
@@ -0,0 +1,346 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * CodecEncoding is used to get the right Codec for a given meta-encoding
+ */
+public class CodecEncoding {
+
+    /**
+     * The canonical encodings are defined to allow a single byte to represent
+     * one of the standard encodings. The following values are defined in the
+     * Pack200 specification, and this array cannot be changed.
+     */
+    private static final BHSDCodec[] canonicalCodec = { null,
+            new BHSDCodec(1, 256), new BHSDCodec(1, 256, 1),
+            new BHSDCodec(1, 256, 0, 1), new BHSDCodec(1, 256, 1, 1),
+            new BHSDCodec(2, 256), new BHSDCodec(2, 256, 1),
+            new BHSDCodec(2, 256, 0, 1), new BHSDCodec(2, 256, 1, 1),
+            new BHSDCodec(3, 256), new BHSDCodec(3, 256, 1),
+            new BHSDCodec(3, 256, 0, 1), new BHSDCodec(3, 256, 1, 1),
+            new BHSDCodec(4, 256), new BHSDCodec(4, 256, 1),
+            new BHSDCodec(4, 256, 0, 1), new BHSDCodec(4, 256, 1, 1),
+            new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1),
+            new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16),
+            new BHSDCodec(5, 16, 1), new BHSDCodec(5, 16, 2),
+            new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1),
+            new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64),
+            new BHSDCodec(5, 64, 1), new BHSDCodec(5, 64, 2),
+            new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1),
+            new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1),
+            new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1),
+            new BHSDCodec(5, 16, 0, 1), new BHSDCodec(5, 16, 1, 1),
+            new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1),
+            new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1),
+            new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1),
+            new BHSDCodec(5, 64, 2, 1), new BHSDCodec(5, 128, 0, 1),
+            new BHSDCodec(5, 128, 1, 1), new BHSDCodec(5, 128, 2, 1),
+            new BHSDCodec(2, 192), new BHSDCodec(2, 224),
+            new BHSDCodec(2, 240), new BHSDCodec(2, 248),
+            new BHSDCodec(2, 252), new BHSDCodec(2, 8, 0, 1),
+            new BHSDCodec(2, 8, 1, 1), new BHSDCodec(2, 16, 0, 1),
+            new BHSDCodec(2, 16, 1, 1), new BHSDCodec(2, 32, 0, 1),
+            new BHSDCodec(2, 32, 1, 1), new BHSDCodec(2, 64, 0, 1),
+            new BHSDCodec(2, 64, 1, 1), new BHSDCodec(2, 128, 0, 1),
+            new BHSDCodec(2, 128, 1, 1), new BHSDCodec(2, 192, 0, 1),
+            new BHSDCodec(2, 192, 1, 1), new BHSDCodec(2, 224, 0, 1),
+            new BHSDCodec(2, 224, 1, 1), new BHSDCodec(2, 240, 0, 1),
+            new BHSDCodec(2, 240, 1, 1), new BHSDCodec(2, 248, 0, 1),
+            new BHSDCodec(2, 248, 1, 1), new BHSDCodec(3, 192),
+            new BHSDCodec(3, 224), new BHSDCodec(3, 240),
+            new BHSDCodec(3, 248), new BHSDCodec(3, 252),
+            new BHSDCodec(3, 8, 0, 1), new BHSDCodec(3, 8, 1, 1),
+            new BHSDCodec(3, 16, 0, 1), new BHSDCodec(3, 16, 1, 1),
+            new BHSDCodec(3, 32, 0, 1), new BHSDCodec(3, 32, 1, 1),
+            new BHSDCodec(3, 64, 0, 1), new BHSDCodec(3, 64, 1, 1),
+            new BHSDCodec(3, 128, 0, 1), new BHSDCodec(3, 128, 1, 1),
+            new BHSDCodec(3, 192, 0, 1), new BHSDCodec(3, 192, 1, 1),
+            new BHSDCodec(3, 224, 0, 1), new BHSDCodec(3, 224, 1, 1),
+            new BHSDCodec(3, 240, 0, 1), new BHSDCodec(3, 240, 1, 1),
+            new BHSDCodec(3, 248, 0, 1), new BHSDCodec(3, 248, 1, 1),
+            new BHSDCodec(4, 192), new BHSDCodec(4, 224),
+            new BHSDCodec(4, 240), new BHSDCodec(4, 248),
+            new BHSDCodec(4, 252), new BHSDCodec(4, 8, 0, 1),
+            new BHSDCodec(4, 8, 1, 1), new BHSDCodec(4, 16, 0, 1),
+            new BHSDCodec(4, 16, 1, 1), new BHSDCodec(4, 32, 0, 1),
+            new BHSDCodec(4, 32, 1, 1), new BHSDCodec(4, 64, 0, 1),
+            new BHSDCodec(4, 64, 1, 1), new BHSDCodec(4, 128, 0, 1),
+            new BHSDCodec(4, 128, 1, 1), new BHSDCodec(4, 192, 0, 1),
+            new BHSDCodec(4, 192, 1, 1), new BHSDCodec(4, 224, 0, 1),
+            new BHSDCodec(4, 224, 1, 1), new BHSDCodec(4, 240, 0, 1),
+            new BHSDCodec(4, 240, 1, 1), new BHSDCodec(4, 248, 0, 1),
+            new BHSDCodec(4, 248, 1, 1) };
+
+    private static Map canonicalCodecsToSpecifiers;
+
+
+    /**
+     * Returns the codec specified by the given value byte and optional byte
+     * header. If the value is >=116, then bytes may be consumed from the
+     * secondary input stream, which is taken to be the contents of the
+     * band_headers byte array. Since the values from this are consumed and not
+     * repeated, the input stream should be reused for subsequent encodings.
+     * This does not therefore close the input stream.
+     *
+     * @param value
+     *            the canonical encoding value
+     * @param in
+     *            the input stream to read additional byte headers from
+     * @param defaultCodec
+     *            TODO
+     * @return the corresponding codec, or <code>null</code> if the default
+     *         should be used
+     *
+     * @throws IOException
+     *             if there is a problem reading from the input stream (which in
+     *             reality, is never, since the band_headers are likely stored
+     *             in a byte array and accessed via a ByteArrayInputStream.
+     *             However, an EOFException could occur if things go wrong)
+     * @throws Pack200Exception
+     */
+    public static Codec getCodec(int value, InputStream in, Codec defaultCodec)
+            throws IOException, Pack200Exception {
+        // Sanity check to make sure that no-one has changed
+        // the canonical codecs, which would really cause havoc
+        if (canonicalCodec.length != 116) {
+            throw new Error(
+                    "Canonical encodings have been incorrectly modified");
+        }
+        if (value < 0) {
+            throw new IllegalArgumentException(
+                    "Encoding cannot be less than zero");
+        } else if (value == 0) {
+            return defaultCodec;
+        } else if (value <= 115) {
+            return canonicalCodec[value];
+        } else if (value == 116) {
+            int code = in.read();
+            if (code == -1) {
+                throw new EOFException(
+                        "End of buffer read whilst trying to decode codec");
+            }
+            int d = (code & 0x01);
+            int s = (code >> 1 & 0x03);
+            int b = (code >> 3 & 0x07) + 1; // this might result in an invalid
+            // number, but it's checked in the
+            // Codec constructor
+            code = in.read();
+            if (code == -1) {
+                throw new EOFException(
+                        "End of buffer read whilst trying to decode codec");
+            }
+            int h = code + 1;
+            // This handles the special cases for invalid combinations of data.
+            return new BHSDCodec(b, h, s, d);
+        } else if (value >= 117 && value <= 140) { // Run codec
+            int offset = value - 117;
+            int kx = offset & 3;
+            boolean kbflag = (offset >> 2 & 1) == 1;
+            boolean adef = (offset >> 3 & 1) == 1;
+            boolean bdef = (offset >> 4 & 1) == 1;
+            // If both A and B use the default encoding, what's the point of
+            // having a run of default values followed by default values
+            if (adef && bdef) {
+                throw new Pack200Exception(
+                        "ADef and BDef should never both be true");
+            }
+            int kb = (kbflag ? in.read() : 3);
+            int k = (kb + 1) * (int) Math.pow(16, kx);
+            Codec aCodec, bCodec;
+            if (adef) {
+                aCodec = defaultCodec;
+            } else {
+                aCodec = getCodec(in.read(), in, defaultCodec);
+            }
+            if (bdef) {
+                bCodec = defaultCodec;
+            } else {
+                bCodec = getCodec(in.read(), in, defaultCodec);
+            }
+            return new RunCodec(k, aCodec, bCodec);
+        } else if (value >= 141 && value <= 188) { // Population Codec
+            int offset = value - 141;
+            boolean fdef = (offset & 1) == 1;
+            boolean udef = (offset >> 1 & 1) == 1;
+            int tdefl = offset >> 2;
+            boolean tdef = tdefl != 0;
+            // From section 6.7.3 of spec
+            final int[] tdefToL = { 0, 4, 8, 16, 32, 64, 128, 192, 224, 240,
+                    248, 252 };
+            int l = tdefToL[tdefl];
+            // NOTE: Do not re-factor this to bring out uCodec; the order in
+            // which
+            // they are read from the stream is important
+            if (tdef) {
+                Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(), in,
+                        defaultCodec));
+                Codec uCodec = (udef ? defaultCodec : getCodec(in.read(), in,
+                        defaultCodec));
+                // Unfortunately, if tdef, then tCodec depends both on l and
+                // also on k, the
+                // number of items read from the fCodec. So we don't know in
+                // advance what
+                // the codec will be.
+                return new PopulationCodec(fCodec, l, uCodec);
+            } else {
+                Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(), in,
+                        defaultCodec));
+                Codec tCodec = getCodec(in.read(), in, defaultCodec);
+                Codec uCodec = (udef ? defaultCodec : getCodec(in.read(), in,
+                        defaultCodec));
+                return new PopulationCodec(fCodec, tCodec, uCodec);
+            }
+        } else {
+            throw new Pack200Exception("Invalid codec encoding byte (" + value
+                    + ") found");
+        }
+    }
+
+    public static int getSpecifierForDefaultCodec(BHSDCodec defaultCodec) {
+        return getSpecifier(defaultCodec, null)[0];
+    }
+
+    public static int[] getSpecifier(Codec codec, Codec defaultForBand) {
+        // lazy initialization
+        if(canonicalCodecsToSpecifiers == null) {
+            HashMap reverseMap = new HashMap(canonicalCodec.length);
+            for (int i = 0; i < canonicalCodec.length; i++) {
+                reverseMap.put(canonicalCodec[i], new Integer(i));
+            }
+            canonicalCodecsToSpecifiers = reverseMap;
+        }
+
+        if(canonicalCodecsToSpecifiers.containsKey(codec)) {
+            return new int[] {((Integer)canonicalCodecsToSpecifiers.get(codec)).intValue()};
+        } else if (codec instanceof BHSDCodec) {
+            // Cache these?
+            BHSDCodec bhsdCodec = (BHSDCodec)codec;
+            int[] specifiers = new int[3];
+            specifiers[0] = 116;
+            specifiers[1] = (bhsdCodec.isDelta() ? 1 : 0) + 2
+                    * bhsdCodec.getS() + 8 * (bhsdCodec.getB()-1);
+            specifiers[2] = bhsdCodec.getH() - 1;
+            return specifiers;
+        } else if (codec instanceof RunCodec) {
+            RunCodec runCodec = (RunCodec) codec;
+            int k = runCodec.getK();
+            int kb;
+            int kx;
+            if(k <= 256) {
+                kb = 0;
+                kx = k - 1;
+            } else if (k <= 4096) {
+                kb = 1;
+                kx = k/16 - 1;
+            } else if (k <= 65536) {
+                kb = 2;
+                kx = k/256 - 1;
+            } else {
+                kb = 3;
+                kx = k/4096 - 1;
+            }
+            Codec aCodec = runCodec.getACodec();
+            Codec bCodec = runCodec.getBCodec();
+            int abDef = 0;
+            if(aCodec.equals(defaultForBand)) {
+                abDef = 1;
+            } else if (bCodec.equals(defaultForBand)) {
+                abDef = 2;
+            }
+            int first = 117 + kb + (kx==3 ? 0 : 4) + (8 * abDef);
+            int[] aSpecifier = abDef == 1 ? new int[0] : getSpecifier(aCodec, defaultForBand);
+            int[] bSpecifier = abDef == 2 ? new int[0] : getSpecifier(bCodec, defaultForBand);
+            int[] specifier = new int[1 + (kx==3 ? 0 : 1) + aSpecifier.length + bSpecifier.length];
+            specifier[0] = first;
+            int index = 1;
+            if(kx != 3) {
+                specifier[1] = kx;
+                index++;
+            }
+            for (int i = 0; i < aSpecifier.length; i++) {
+                specifier[index] = aSpecifier[i];
+                index++;
+            }
+            for (int i = 0; i < bSpecifier.length; i++) {
+                specifier[index] = bSpecifier[i];
+                index++;
+            }
+            return specifier;
+        } else if (codec instanceof PopulationCodec) {
+            PopulationCodec populationCodec = (PopulationCodec) codec;
+            Codec tokenCodec = populationCodec.getTokenCodec();
+            Codec favouredCodec = populationCodec.getFavouredCodec();
+            Codec unfavouredCodec = populationCodec.getUnfavouredCodec();
+            int fDef = favouredCodec.equals(defaultForBand) ? 1 : 0;
+            int uDef = unfavouredCodec.equals(defaultForBand) ? 1 : 0;
+            int tDefL = 0;
+            int[] favoured = populationCodec.getFavoured();
+            if(favoured != null) {
+                int k = favoured.length;
+                if(tokenCodec == Codec.BYTE1) {
+                    tDefL = 1;
+                } else if (tokenCodec instanceof BHSDCodec) {
+                    BHSDCodec tokenBHSD = (BHSDCodec) tokenCodec;
+                    if(tokenBHSD.getS() == 0) {
+                        int[] possibleLValues = new int[] {4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252};
+                        int l = 256-tokenBHSD.getH();
+                        int index = Arrays.binarySearch(possibleLValues, l);
+                        if(index != -1) {
+                            // TODO: check range is ok for ks
+                            tDefL = index++;
+                        }
+                    }
+                }
+            }
+            int first = 141 + fDef + (2 * uDef) + (4 * tDefL);
+            int[] favouredSpecifier = fDef == 1 ? new int[0] : getSpecifier(favouredCodec, defaultForBand);
+            int[] tokenSpecifier = tDefL != 0 ? new int[0] : getSpecifier(tokenCodec, defaultForBand);
+            int[] unfavouredSpecifier = uDef == 1 ? new int[0] : getSpecifier(unfavouredCodec, defaultForBand);
+            int[] specifier = new int[1 + favouredSpecifier.length + unfavouredSpecifier.length + tokenSpecifier.length];
+            specifier[0] = first;
+            int index = 1;
+            for (int i = 0; i < favouredSpecifier.length; i++) {
+                specifier[index] = favouredSpecifier[i];
+                index++;
+            }
+            for (int i = 0; i < tokenSpecifier.length; i++) {
+                specifier[index] = tokenSpecifier[i];
+                index++;
+            }
+            for (int i = 0; i < unfavouredSpecifier.length; i++) {
+                specifier[index] = unfavouredSpecifier[i];
+                index++;
+            }
+            return specifier;
+        }
+
+        return null;
+    }
+
+    public static BHSDCodec getCanonicalCodec(int i) {
+        return canonicalCodec[i];
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java b/src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java
new file mode 100644
index 0000000..d2fd1b3
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/ConstantPoolEntry.java
@@ -0,0 +1,34 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Abstract superclass for constant pool entries
+ */
+public abstract class ConstantPoolEntry {
+
+    private int index = -1;
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/CpBands.java b/src/main/java/org/apache/harmony/pack200/CpBands.java
new file mode 100644
index 0000000..aad5037
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/CpBands.java
@@ -0,0 +1,686 @@
+/*
+ * 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.objectweb.asm.Type;
+
+/**
+ * Pack200 Constant Pool Bands
+ */
+public class CpBands extends BandSet {
+
+    // Don't need to include default attribute names in the constant pool bands
+    private final Set defaultAttributeNames = new HashSet();
+
+    private final Set cp_Utf8 = new TreeSet();
+    private final Set cp_Int = new TreeSet();
+    private final Set cp_Float = new TreeSet();
+    private final Set cp_Long = new TreeSet();
+    private final Set cp_Double = new TreeSet();
+    private final Set cp_String = new TreeSet();
+    private final Set cp_Class = new TreeSet();
+    private final Set cp_Signature = new TreeSet();
+    private final Set cp_Descr = new TreeSet();
+    private final Set cp_Field = new TreeSet();
+    private final Set cp_Method = new TreeSet();
+    private final Set cp_Imethod = new TreeSet();
+
+    private final Map stringsToCpUtf8 = new HashMap();
+    private final Map stringsToCpNameAndType = new HashMap();
+    private final Map stringsToCpClass = new HashMap();
+    private final Map stringsToCpSignature = new HashMap();
+    private final Map stringsToCpMethod = new HashMap();
+    private final Map stringsToCpField = new HashMap();
+    private final Map stringsToCpIMethod = new HashMap();
+
+    private final Map objectsToCPConstant = new HashMap();
+
+    private final Segment segment;
+
+    public CpBands(Segment segment, int effort) {
+        super(effort, segment.getSegmentHeader());
+        this.segment = segment;
+        defaultAttributeNames.add("AnnotationDefault");
+        defaultAttributeNames.add("RuntimeVisibleAnnotations");
+        defaultAttributeNames.add("RuntimeInvisibleAnnotations");
+        defaultAttributeNames.add("RuntimeVisibleParameterAnnotations");
+        defaultAttributeNames.add("RuntimeInvisibleParameterAnnotations");
+        defaultAttributeNames.add("Code");
+        defaultAttributeNames.add("LineNumberTable");
+        defaultAttributeNames.add("LocalVariableTable");
+        defaultAttributeNames.add("LocalVariableTypeTable");
+        defaultAttributeNames.add("ConstantValue");
+        defaultAttributeNames.add("Deprecated");
+        defaultAttributeNames.add("EnclosingMethod");
+        defaultAttributeNames.add("Exceptions");
+        defaultAttributeNames.add("InnerClasses");
+        defaultAttributeNames.add("Signature");
+        defaultAttributeNames.add("SourceFile");
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing constant pool bands...");
+        writeCpUtf8(out);
+        writeCpInt(out);
+        writeCpFloat(out);
+        writeCpLong(out);
+        writeCpDouble(out);
+        writeCpString(out);
+        writeCpClass(out);
+        writeCpSignature(out);
+        writeCpDescr(out);
+        writeCpMethodOrField(cp_Field, out, "cp_Field");
+        writeCpMethodOrField(cp_Method, out, "cp_Method");
+        writeCpMethodOrField(cp_Imethod, out, "cp_Imethod");
+    }
+
+    private void writeCpUtf8(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Utf8.size() + " UTF8 entries...");
+        int[] cpUtf8Prefix = new int[cp_Utf8.size() - 2];
+        int[] cpUtf8Suffix = new int[cp_Utf8.size() - 1];
+        List chars = new ArrayList();
+        List bigSuffix = new ArrayList();
+        List bigChars = new ArrayList();
+        Object[] cpUtf8Array = cp_Utf8.toArray();
+        String first = ((CPUTF8) cpUtf8Array[1]).getUnderlyingString();
+        cpUtf8Suffix[0] = first.length();
+        addCharacters(chars, first.toCharArray());
+        for (int i = 2; i < cpUtf8Array.length; i++) {
+            char[] previous = ((CPUTF8) cpUtf8Array[i - 1])
+                    .getUnderlyingString().toCharArray();
+            String currentStr = ((CPUTF8) cpUtf8Array[i]).getUnderlyingString();
+            char[] current = currentStr.toCharArray();
+            int prefix = 0;
+            for (int j = 0; j < previous.length; j++) {
+                if (previous[j] == current[j]) {
+                    prefix++;
+                } else {
+                    break;
+                }
+            }
+            cpUtf8Prefix[i - 2] = prefix;
+            currentStr = currentStr.substring(prefix);
+            char[] suffix = currentStr.toCharArray();
+            if (suffix.length > 1000) { // big suffix (1000 is arbitrary - can we
+                // do better?)
+                cpUtf8Suffix[i - 1] = 0;
+                bigSuffix.add(new Integer(suffix.length));
+                addCharacters(bigChars, suffix);
+            } else {
+                cpUtf8Suffix[i - 1] = suffix.length;
+                addCharacters(chars, suffix);
+            }
+        }
+        int[] cpUtf8Chars = new int[chars.size()];
+        int[] cpUtf8BigSuffix = new int[bigSuffix.size()];
+        int[][] cpUtf8BigChars = new int[bigSuffix.size()][];
+        for (int i = 0; i < cpUtf8Chars.length; i++) {
+            cpUtf8Chars[i] = ((Character) chars.get(i)).charValue();
+        }
+        for (int i = 0; i < cpUtf8BigSuffix.length; i++) {
+            int numBigChars = ((Integer) bigSuffix.get(i)).intValue();
+            cpUtf8BigSuffix[i] = numBigChars;
+            cpUtf8BigChars[i] = new int[numBigChars];
+            for (int j = 0; j < numBigChars; j++) {
+                cpUtf8BigChars[i][j] = ((Character) bigChars.remove(0)).charValue();
+            }
+        }
+
+        byte[] encodedBand = encodeBandInt("cpUtf8Prefix", cpUtf8Prefix, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpUtf8Prefix[" + cpUtf8Prefix.length + "]");
+
+        encodedBand = encodeBandInt("cpUtf8Suffix", cpUtf8Suffix, Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpUtf8Suffix[" + cpUtf8Suffix.length + "]");
+
+        encodedBand = encodeBandInt("cpUtf8Chars", cpUtf8Chars, Codec.CHAR3);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpUtf8Chars[" + cpUtf8Chars.length + "]");
+
+        encodedBand = encodeBandInt("cpUtf8BigSuffix", cpUtf8BigSuffix,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpUtf8BigSuffix[" + cpUtf8BigSuffix.length + "]");
+
+        for (int i = 0; i < cpUtf8BigChars.length; i++) {
+            encodedBand = encodeBandInt("cpUtf8BigChars " + i,
+                    cpUtf8BigChars[i], Codec.DELTA5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length
+                    + " bytes from cpUtf8BigChars" + i + "["
+                    + cpUtf8BigChars[i].length + "]");
+        }
+    }
+
+    private void addCharacters(List chars, char[] charArray) {
+        for (int i = 0; i < charArray.length; i++) {
+            chars.add(new Character(charArray[i]));
+        }
+    }
+
+    private void writeCpInt(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Int.size() + " Integer entries...");
+        int[] cpInt = new int[cp_Int.size()];
+        int i = 0;
+        for (Iterator iterator = cp_Int.iterator(); iterator.hasNext();) {
+            CPInt integer = (CPInt) iterator.next();
+            cpInt[i] = integer.getInt();
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt("cp_Int", cpInt, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Int[" + cpInt.length + "]");
+    }
+
+    private void writeCpFloat(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Float.size() + " Float entries...");
+        int[] cpFloat = new int[cp_Float.size()];
+        int i = 0;
+        for (Iterator iterator = cp_Float.iterator(); iterator.hasNext();) {
+            CPFloat fl = (CPFloat) iterator.next();
+            cpFloat[i] = Float.floatToIntBits(fl.getFloat());
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt("cp_Float", cpFloat, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Float[" + cpFloat.length + "]");
+    }
+
+    private void writeCpLong(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Long.size() + " Long entries...");
+        int[] highBits = new int[cp_Long.size()];
+        int[] loBits = new int[cp_Long.size()];
+        int i = 0;
+        for (Iterator iterator = cp_Long.iterator(); iterator.hasNext();) {
+            CPLong lng = (CPLong) iterator.next();
+            long l = lng.getLong();
+            highBits[i] = (int) (l >> 32);
+            loBits[i] = (int) l;
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt("cp_Long_hi", highBits, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Long_hi[" + highBits.length + "]");
+
+        encodedBand = encodeBandInt("cp_Long_lo", loBits, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Long_lo[" + loBits.length + "]");
+    }
+
+    private void writeCpDouble(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Double.size() + " Double entries...");
+        int[] highBits = new int[cp_Double.size()];
+        int[] loBits = new int[cp_Double.size()];
+        int i = 0;
+        for (Iterator iterator = cp_Double.iterator(); iterator.hasNext();) {
+            CPDouble dbl = (CPDouble) iterator.next();
+            long l = Double.doubleToLongBits(dbl.getDouble());
+            highBits[i] = (int) (l >> 32);
+            loBits[i] = (int) l;
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt("cp_Double_hi", highBits, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Double_hi[" + highBits.length + "]");
+
+        encodedBand = encodeBandInt("cp_Double_lo", loBits, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Double_lo[" + loBits.length + "]");
+    }
+
+    private void writeCpString(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_String.size() + " String entries...");
+        int[] cpString = new int[cp_String.size()];
+        int i = 0;
+        for (Iterator iterator = cp_String.iterator(); iterator.hasNext();) {
+            CPString cpStr = (CPString) iterator.next();
+            cpString[i] = cpStr.getIndexInCpUtf8();
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt("cpString", cpString, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpString[" + cpString.length + "]");
+    }
+
+    private void writeCpClass(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Class.size() + " Class entries...");
+        int[] cpClass = new int[cp_Class.size()];
+        int i = 0;
+        for (Iterator iterator = cp_Class.iterator(); iterator.hasNext();) {
+            CPClass cpCl = (CPClass) iterator.next();
+            cpClass[i] = cpCl.getIndexInCpUtf8();
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt("cpClass", cpClass, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpClass[" + cpClass.length + "]");
+    }
+
+    private void writeCpSignature(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Signature.size() + " Signature entries...");
+        int[] cpSignatureForm = new int[cp_Signature.size()];
+        List classes = new ArrayList();
+        int i = 0;
+        for (Iterator iterator = cp_Signature.iterator(); iterator.hasNext();) {
+            CPSignature cpS = (CPSignature) iterator.next();
+            classes.addAll(cpS.getClasses());
+            cpSignatureForm[i] = cpS.getIndexInCpUtf8();
+            i++;
+        }
+        int[] cpSignatureClasses = new int[classes.size()];
+        for (int j = 0; j < cpSignatureClasses.length; j++) {
+            cpSignatureClasses[j] = ((CPClass) classes.get(j)).getIndex();
+        }
+
+        byte[] encodedBand = encodeBandInt("cpSignatureForm", cpSignatureForm,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpSignatureForm[" + cpSignatureForm.length + "]");
+
+        encodedBand = encodeBandInt("cpSignatureClasses", cpSignatureClasses,
+                Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cpSignatureClasses[" + cpSignatureClasses.length + "]");
+    }
+
+    private void writeCpDescr(OutputStream out) throws IOException,
+            Pack200Exception {
+        PackingUtils.log("Writing " + cp_Descr.size()
+                + " Descriptor entries...");
+        int[] cpDescrName = new int[cp_Descr.size()];
+        int[] cpDescrType = new int[cp_Descr.size()];
+        int i = 0;
+        for (Iterator iterator = cp_Descr.iterator(); iterator.hasNext();) {
+            CPNameAndType nameAndType = (CPNameAndType) iterator.next();
+            cpDescrName[i] = nameAndType.getNameIndex();
+            cpDescrType[i] = nameAndType.getTypeIndex();
+            i++;
+        }
+
+        byte[] encodedBand = encodeBandInt("cp_Descr_Name", cpDescrName,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Descr_Name[" + cpDescrName.length + "]");
+
+        encodedBand = encodeBandInt("cp_Descr_Type", cpDescrType, Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from cp_Descr_Type[" + cpDescrType.length + "]");
+    }
+
+    private void writeCpMethodOrField(Set cp, OutputStream out, String name)
+            throws IOException, Pack200Exception {
+        PackingUtils.log("Writing " + cp.size()
+                + " Method and Field entries...");
+        int[] cp_methodOrField_class = new int[cp.size()];
+        int[] cp_methodOrField_desc = new int[cp.size()];
+        int i = 0;
+        for (Iterator iterator = cp.iterator(); iterator.hasNext();) {
+            CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
+            cp_methodOrField_class[i] = mOrF.getClassIndex();
+            cp_methodOrField_desc[i] = mOrF.getDescIndex();
+            i++;
+        }
+        byte[] encodedBand = encodeBandInt(name + "_class",
+                cp_methodOrField_class, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                + name + "_class[" + cp_methodOrField_class.length + "]");
+
+        encodedBand = encodeBandInt(name + "_desc", cp_methodOrField_desc,
+                Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                + name + "_desc[" + cp_methodOrField_desc.length + "]");
+    }
+
+    /**
+     * All input classes for the segment have now been read in, so this method
+     * is called so that this class can calculate/complete anything it could not
+     * do while classes were being read.
+     */
+    public void finaliseBands() {
+        addCPUtf8("");
+        removeSignaturesFromCpUTF8();
+        addIndices();
+        segmentHeader.setCp_Utf8_count(cp_Utf8.size());
+        segmentHeader.setCp_Int_count(cp_Int.size());
+        segmentHeader.setCp_Float_count(cp_Float.size());
+        segmentHeader.setCp_Long_count(cp_Long.size());
+        segmentHeader.setCp_Double_count(cp_Double.size());
+        segmentHeader.setCp_String_count(cp_String.size());
+        segmentHeader.setCp_Class_count(cp_Class.size());
+        segmentHeader.setCp_Signature_count(cp_Signature.size());
+        segmentHeader.setCp_Descr_count(cp_Descr.size());
+        segmentHeader.setCp_Field_count(cp_Field.size());
+        segmentHeader.setCp_Method_count(cp_Method.size());
+        segmentHeader.setCp_Imethod_count(cp_Imethod.size());
+    }
+
+    private void removeSignaturesFromCpUTF8() {
+        for (Iterator iterator = cp_Signature.iterator(); iterator.hasNext();) {
+            CPSignature signature = (CPSignature) iterator.next();
+            String sigStr = signature.getUnderlyingString();
+            CPUTF8 utf8 = signature.getSignatureForm();
+            String form = utf8.getUnderlyingString();
+            if (!sigStr.equals(form)) {
+                removeCpUtf8(sigStr);
+            }
+        }
+    }
+
+    private void addIndices() {
+        Set[] sets = new Set[] { cp_Utf8, cp_Int, cp_Float, cp_Long, cp_Double,
+                cp_String, cp_Class, cp_Signature, cp_Descr, cp_Field,
+                cp_Method, cp_Imethod };
+        for (int i = 0; i < sets.length; i++) {
+            int j = 0;
+            for (Iterator iterator = sets[i].iterator(); iterator.hasNext();) {
+                ConstantPoolEntry entry = (ConstantPoolEntry) iterator.next();
+                entry.setIndex(j);
+                j++;
+            }
+        }
+        Map classNameToIndex = new HashMap();
+        for (Iterator iterator = cp_Field.iterator(); iterator.hasNext();) {
+            CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
+            CPClass className = mOrF.getClassName();
+            Integer index = (Integer) classNameToIndex.get(className);
+            if (index == null) {
+                classNameToIndex.put(className, new Integer(1));
+                mOrF.setIndexInClass(0);
+            } else {
+                int theIndex = index.intValue();
+                mOrF.setIndexInClass(theIndex);
+                classNameToIndex.put(className, new Integer(theIndex + 1));
+            }
+        }
+        classNameToIndex.clear();
+        Map classNameToConstructorIndex = new HashMap();
+        for (Iterator iterator = cp_Method.iterator(); iterator.hasNext();) {
+            CPMethodOrField mOrF = (CPMethodOrField) iterator.next();
+            CPClass className = mOrF.getClassName();
+            Integer index = (Integer) classNameToIndex.get(className);
+            if (index == null) {
+                classNameToIndex.put(className, new Integer(1));
+                mOrF.setIndexInClass(0);
+            } else {
+                int theIndex = index.intValue();
+                mOrF.setIndexInClass(theIndex);
+                classNameToIndex.put(className, new Integer(theIndex + 1));
+            }
+            if(mOrF.getDesc().getName().equals("<init>")) {
+                Integer constructorIndex = (Integer) classNameToConstructorIndex.get(className);
+                if (constructorIndex == null) {
+                    classNameToConstructorIndex.put(className, new Integer(1));
+                    mOrF.setIndexInClassForConstructor(0);
+                } else {
+                    int theIndex = constructorIndex.intValue();
+                    mOrF.setIndexInClassForConstructor(theIndex);
+                    classNameToConstructorIndex.put(className, new Integer(theIndex + 1));
+                }
+            }
+        }
+    }
+
+    private void removeCpUtf8(String string) {
+        CPUTF8 utf8 = (CPUTF8) stringsToCpUtf8.get(string);
+        if (utf8 != null) {
+            if(stringsToCpClass.get(string) == null) { // don't remove if strings are also in cpclass
+                stringsToCpUtf8.remove(string);
+                cp_Utf8.remove(utf8);
+            }
+        }
+    }
+
+    void addCPUtf8(String utf8) {
+        getCPUtf8(utf8);
+    }
+
+    public CPUTF8 getCPUtf8(String utf8) {
+        if(utf8 == null) {
+            return null;
+        }
+        CPUTF8 cpUtf8 = (CPUTF8) stringsToCpUtf8.get(utf8);
+        if (cpUtf8 == null) {
+            cpUtf8 = new CPUTF8(utf8);
+            cp_Utf8.add(cpUtf8);
+            stringsToCpUtf8.put(utf8, cpUtf8);
+        }
+        return cpUtf8;
+    }
+
+    public CPSignature getCPSignature(String signature) {
+        if(signature == null) {
+            return null;
+        }
+        CPSignature cpS = (CPSignature) stringsToCpSignature.get(signature);
+        if (cpS == null) {
+            List cpClasses = new ArrayList();
+            CPUTF8 signatureUTF8;
+            if (signature.length() > 1 && signature.indexOf('L') != -1) {
+                List classes = new ArrayList();
+                char[] chars = signature.toCharArray();
+                StringBuffer signatureString = new StringBuffer();
+                for (int i = 0; i < chars.length; i++) {
+                    signatureString.append(chars[i]);
+                    if (chars[i] == 'L') {
+                        StringBuffer className = new StringBuffer();
+                        for (int j = i + 1; j < chars.length; j++) {
+                            char c = chars[j];
+                            if (Character.isLetter(c) || Character.isDigit(c)
+                                    || c == '/' || c == '$' || c == '_') {
+                                className.append(c);
+                            } else {
+                                classes.add(className.toString());
+                                i = j - 1;
+                                break;
+                            }
+                        }
+                    }
+                }
+                removeCpUtf8(signature);
+                for (Iterator iterator2 = classes.iterator(); iterator2
+                        .hasNext();) {
+                    String className = (String) iterator2.next();
+                    CPClass cpClass = null;
+                    if(className!= null) {
+                        className = className.replace('.', '/');
+                        cpClass = (CPClass) stringsToCpClass.get(className);
+                        if (cpClass == null) {
+                            CPUTF8 cpUtf8 = getCPUtf8(className);
+                            cpClass = new CPClass(cpUtf8);
+                            cp_Class.add(cpClass);
+                            stringsToCpClass.put(className, cpClass);
+                        }
+                    }
+                    cpClasses.add(cpClass);
+                }
+
+                signatureUTF8 = getCPUtf8(signatureString.toString());
+            } else {
+                signatureUTF8 = getCPUtf8(signature);
+            }
+            cpS = new CPSignature(signature, signatureUTF8, cpClasses);
+            cp_Signature.add(cpS);
+            stringsToCpSignature.put(signature, cpS);
+        }
+        return cpS;
+    }
+
+    public CPClass getCPClass(String className) {
+        if(className == null) {
+            return null;
+        }
+        className = className.replace('.', '/');
+        CPClass cpClass = (CPClass) stringsToCpClass.get(className);
+        if (cpClass == null) {
+            CPUTF8 cpUtf8 = getCPUtf8(className);
+            cpClass = new CPClass(cpUtf8);
+            cp_Class.add(cpClass);
+            stringsToCpClass.put(className, cpClass);
+        }
+        if(cpClass.isInnerClass()) {
+            segment.getClassBands().currentClassReferencesInnerClass(cpClass);
+        }
+        return cpClass;
+    }
+
+    public void addCPClass(String className) {
+        getCPClass(className);
+    }
+
+    public CPNameAndType getCPNameAndType(String name, String signature) {
+        String descr = name + ":" + signature;
+        CPNameAndType nameAndType = (CPNameAndType) stringsToCpNameAndType
+                .get(descr);
+        if (nameAndType == null) {
+            nameAndType = new CPNameAndType(getCPUtf8(name),
+                    getCPSignature(signature));
+            stringsToCpNameAndType.put(descr, nameAndType);
+            cp_Descr.add(nameAndType);
+        }
+        return nameAndType;
+    }
+
+    public CPMethodOrField getCPField(CPClass cpClass, String name, String desc) {
+        String key = cpClass.toString() + ":" + name + ":" + desc;
+        CPMethodOrField cpF = (CPMethodOrField) stringsToCpField
+                .get(key);
+        if (cpF == null) {
+            CPNameAndType nAndT = getCPNameAndType(name, desc);
+            cpF = new CPMethodOrField(cpClass, nAndT);
+            cp_Field.add(cpF);
+            stringsToCpField.put(key, cpF);
+        }
+        return cpF;
+    }
+
+    public CPConstant getConstant(Object value) {
+        CPConstant constant = (CPConstant) objectsToCPConstant.get(value);
+        if (constant == null) {
+            if (value instanceof Integer) {
+                constant = new CPInt(((Integer) value).intValue());
+                cp_Int.add(constant);
+            } else if (value instanceof Long) {
+                constant = new CPLong(((Long) value).longValue());
+                cp_Long.add(constant);
+            } else if (value instanceof Float) {
+                constant = new CPFloat(((Float) value).floatValue());
+                cp_Float.add(constant);
+            } else if (value instanceof Double) {
+                constant = new CPDouble(((Double) value).doubleValue());
+                cp_Double.add(constant);
+            } else if (value instanceof String) {
+                constant = new CPString(getCPUtf8((String) value));
+                cp_String.add(constant);
+            } else if (value instanceof Type) {
+                String className = ((Type) value).getClassName();
+                if(className.endsWith("[]")) {
+                    className = "[L" + className.substring(0, className.length() - 2);
+                    while(className.endsWith("[]")) {
+                        className = "[" + className.substring(0, className.length() - 2);
+                    }
+                    className += ";";
+                }
+                constant = getCPClass(className);
+            }
+            objectsToCPConstant.put(value, constant);
+        }
+        return constant;
+    }
+
+    public CPMethodOrField getCPMethod(CPClass cpClass, String name, String desc) {
+        String key = cpClass.toString() + ":" + name + ":" + desc;
+        CPMethodOrField cpM = (CPMethodOrField) stringsToCpMethod
+                .get(key);
+        if (cpM == null) {
+            CPNameAndType nAndT = getCPNameAndType(name, desc);
+            cpM = new CPMethodOrField(cpClass, nAndT);
+            cp_Method.add(cpM);
+            stringsToCpMethod.put(key, cpM);
+        }
+        return cpM;
+    }
+
+    public CPMethodOrField getCPIMethod(CPClass cpClass, String name,
+            String desc) {
+        String key = cpClass.toString() + ":" + name + ":" + desc;
+        CPMethodOrField cpIM = (CPMethodOrField) stringsToCpIMethod
+                .get(key);
+        if (cpIM == null) {
+            CPNameAndType nAndT = getCPNameAndType(name, desc);
+            cpIM = new CPMethodOrField(cpClass, nAndT);
+            cp_Imethod.add(cpIM);
+            stringsToCpIMethod.put(key, cpIM);
+        }
+        return cpIM;
+    }
+
+    public CPMethodOrField getCPField(String owner, String name, String desc) {
+        return getCPField(getCPClass(owner), name, desc);
+    }
+
+    public CPMethodOrField getCPMethod(String owner, String name, String desc) {
+        return getCPMethod(getCPClass(owner), name, desc);
+    }
+
+    public CPMethodOrField getCPIMethod(String owner, String name, String desc) {
+        return getCPIMethod(getCPClass(owner), name, desc);
+    }
+
+    public boolean existsCpClass(String className) {
+        CPClass cpClass = (CPClass) stringsToCpClass.get(className);
+        return cpClass != null;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/FileBands.java b/src/main/java/org/apache/harmony/pack200/FileBands.java
new file mode 100644
index 0000000..be9cc00
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/FileBands.java
@@ -0,0 +1,183 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.apache.harmony.pack200.Archive.PackingFile;
+import org.apache.harmony.pack200.Archive.SegmentUnit;
+import org.objectweb.asm.ClassReader;
+
+/**
+ * Bands containing information about files in the pack200 archive and the file
+ * contents for non-class-files. Corresponds to the <code>file_bands</code> set
+ * of bands described in the specification.
+ */
+public class FileBands extends BandSet {
+
+    private final CPUTF8[] fileName;
+    private int[] file_name;
+    private final int[] file_modtime;
+    private final long[] file_size;
+    private final int[] file_options;
+    private final byte[][] file_bits;
+    private final List fileList;
+    private final PackingOptions options;
+    private final CpBands cpBands;
+
+    public FileBands(CpBands cpBands, SegmentHeader segmentHeader,
+            PackingOptions options, SegmentUnit segmentUnit, int effort) {
+        super(effort, segmentHeader);
+        fileList = segmentUnit.getFileList();
+        this.options = options;
+        this.cpBands = cpBands;
+        int size = fileList.size();
+        fileName = new CPUTF8[size];
+        file_modtime = new int[size];
+        file_size = new long[size];
+        file_options = new int[size];
+        int totalSize = 0;
+        file_bits = new byte[size][];
+        int archiveModtime = segmentHeader.getArchive_modtime();
+
+        Set classNames = new HashSet();
+        for (Iterator iterator = segmentUnit.getClassList().iterator(); iterator
+                .hasNext();) {
+            ClassReader reader = (ClassReader) iterator.next();
+            classNames.add(reader.getClassName());
+        }
+        CPUTF8 emptyString = cpBands.getCPUtf8("");
+        long modtime;
+        int latestModtime = Integer.MIN_VALUE;
+        boolean isLatest = !PackingOptions.KEEP.equals(options
+                .getModificationTime());
+        for (int i = 0; i < size; i++) {
+            PackingFile packingFile = (PackingFile) fileList.get(i);
+            String name = packingFile.getName();
+            if (name.endsWith(".class") && !options.isPassFile(name)) {
+                file_options[i] |= (1 << 1);
+                if (classNames.contains(name.substring(0, name.length() - 6))) {
+                    fileName[i] = emptyString;
+                } else {
+                    fileName[i] = cpBands.getCPUtf8(name);
+                }
+            } else {
+                fileName[i] = cpBands.getCPUtf8(name);
+            }
+            // set deflate_hint for file element
+            if (options.isKeepDeflateHint() && packingFile.isDefalteHint()) {
+                file_options[i] |= 0x1;
+            }
+            byte[] bytes = packingFile.getContents();
+            file_size[i] = bytes.length;
+            totalSize += file_size[i];
+
+            // update modification time
+            modtime = (packingFile.getModtime() + TimeZone.getDefault().getRawOffset()) / 1000L;
+            file_modtime[i] = (int) (modtime - archiveModtime);
+            if (isLatest && latestModtime < file_modtime[i]) {
+                latestModtime = file_modtime[i];
+            }
+
+            file_bits[i] = packingFile.getContents();
+        }
+
+        if (isLatest) {
+            for (int index = 0; index < size; index++) {
+                file_modtime[index] = latestModtime;
+            }
+        }
+    }
+
+    /**
+     * All input classes for the segment have now been read in, so this method
+     * is called so that this class can calculate/complete anything it could not
+     * do while classes were being read.
+     */
+    public void finaliseBands() {
+        file_name = new int[fileName.length];
+        for (int i = 0; i < file_name.length; i++) {
+            if (fileName[i].equals(cpBands.getCPUtf8(""))) {
+                PackingFile packingFile = (PackingFile) fileList.get(i);
+                String name = packingFile.getName();
+                if (options.isPassFile(name)) {
+                    fileName[i] = cpBands.getCPUtf8(name);
+                    file_options[i] &= (1 << 1) ^ 0xFFFFFFFF;
+                }
+            }
+            file_name[i] = fileName[i].getIndex();
+        }
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing file bands...");
+        byte[] encodedBand = encodeBandInt("file_name", file_name,
+                Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from file_name[" + file_name.length + "]");
+
+        encodedBand = encodeFlags("file_size", file_size, Codec.UNSIGNED5,
+                Codec.UNSIGNED5, segmentHeader.have_file_size_hi());
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from file_size[" + file_size.length + "]");
+
+        if (segmentHeader.have_file_modtime()) {
+            encodedBand = encodeBandInt("file_modtime", file_modtime,
+                    Codec.DELTA5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length
+                    + " bytes from file_modtime[" + file_modtime.length + "]");
+        }
+        if (segmentHeader.have_file_options()) {
+            encodedBand = encodeBandInt("file_options", file_options,
+                    Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length
+                    + " bytes from file_options[" + file_options.length + "]");
+        }
+
+        encodedBand = encodeBandInt("file_bits", flatten(file_bits),
+                Codec.BYTE1);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from file_bits[" + file_bits.length + "]");
+    }
+
+    private int[] flatten(byte[][] bytes) {
+        int total = 0;
+        for (int i = 0; i < bytes.length; i++) {
+            total += bytes[i].length;
+        }
+        int[] band = new int[total];
+        int index = 0;
+        for (int i = 0; i < bytes.length; i++) {
+            for (int j = 0; j < bytes[i].length; j++) {
+                band[index++] = bytes[i][j] & 0xFF;
+            }
+        }
+        return band;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/IcBands.java b/src/main/java/org/apache/harmony/pack200/IcBands.java
new file mode 100644
index 0000000..8eac217
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/IcBands.java
@@ -0,0 +1,199 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Inner class bands (corresponds to the <code>ic_bands</code> set of bands in
+ * the pack200 specification)
+ */
+public class IcBands extends BandSet {
+
+    private final Set innerClasses = new TreeSet();
+    private final CpBands cpBands;
+    private int bit16Count = 0;
+
+    private final Map outerToInner = new HashMap();
+
+    public IcBands(SegmentHeader segmentHeader, CpBands cpBands, int effort) {
+        super(effort, segmentHeader);
+        this.cpBands = cpBands;
+    }
+
+    /**
+     * All input classes for the segment have now been read in, so this method
+     * is called so that this class can calculate/complete anything it could not
+     * do while classes were being read.
+     */
+    public void finaliseBands() {
+        segmentHeader.setIc_count(innerClasses.size());
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing internal class bands...");
+        int[] ic_this_class = new int[innerClasses.size()];
+        int[] ic_flags = new int[innerClasses.size()];
+        int[] ic_outer_class = new int[bit16Count];
+        int[] ic_name = new int[bit16Count];
+
+        int index2 = 0;
+        List innerClassesList = new ArrayList(innerClasses);
+        for (int i = 0; i < ic_this_class.length; i++) {
+            IcTuple icTuple = (IcTuple) innerClassesList.get(i);
+            ic_this_class[i] = icTuple.C.getIndex();
+            ic_flags[i] = icTuple.F;
+            if((icTuple.F & (1<<16)) != 0) {
+                ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1;
+                ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1;
+                index2++;
+            }
+        }
+        byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class,
+                Codec.UDELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from ic_this_class[" + ic_this_class.length + "]");
+
+        encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from ic_flags[" + ic_flags.length + "]");
+
+        encodedBand = encodeBandInt("ic_outer_class", ic_outer_class,
+                Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from ic_outer_class[" + ic_outer_class.length + "]");
+
+        encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5);
+        out.write(encodedBand);
+        PackingUtils.log("Wrote " + encodedBand.length
+                + " bytes from ic_name[" + ic_name.length + "]");
+    }
+
+    public void addInnerClass(String name, String outerName, String innerName,
+            int flags) {
+        if(outerName != null || innerName != null) {
+            if(namesArePredictable(name, outerName, innerName)) {
+                IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
+                addToMap(outerName, innerClass);
+                innerClasses.add(innerClass);
+            } else {
+                flags |= (1<<16);
+                IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), cpBands.getCPUtf8(innerName));
+                boolean added = innerClasses.add(icTuple);
+                if(added) {
+                    bit16Count++;
+                    addToMap(outerName, icTuple);
+                }
+            }
+        } else {
+            IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
+            addToMap(getOuter(name), innerClass);
+            innerClasses.add(innerClass);
+        }
+    }
+
+    public List getInnerClassesForOuter(String outerClassName) {
+        return (List) outerToInner.get(outerClassName);
+    }
+
+    private String getOuter(String name) {
+        return name.substring(0, name.lastIndexOf('$'));
+    }
+
+    private void addToMap(String outerName, IcTuple icTuple) {
+        List tuples = (List) outerToInner.get(outerName);
+        if(tuples == null) {
+            tuples = new ArrayList();
+            outerToInner.put(outerName, tuples);
+            tuples.add(icTuple);
+        } else {
+            for (Iterator iterator = tuples.iterator(); iterator.hasNext();) {
+                IcTuple icT = (IcTuple) iterator.next();
+                if(icTuple.equals(icT)) {
+                    return;
+                }
+            }
+            tuples.add(icTuple);
+        }
+    }
+
+    private boolean namesArePredictable(String name, String outerName,
+            String innerName) {
+        // TODO: Could be multiple characters, not just $
+        return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1;
+    }
+
+    class IcTuple implements Comparable {
+
+        protected CPClass C; // this class
+        protected int F; // flags
+        protected CPClass C2; // outer class
+        protected CPUTF8 N; // name
+
+        public IcTuple(CPClass C, int F, CPClass C2, CPUTF8 N) {
+            this.C = C;
+            this.F = F;
+            this.C2 = C2;
+            this.N = N;
+        }
+
+        public boolean equals(Object o) {
+            if(o instanceof IcTuple) {
+                IcTuple icT = (IcTuple)o;
+                return C.equals(icT.C) && F == icT.F && (C2 != null ? C2.equals(icT.C2) : icT.C2 == null) && (N != null ? N.equals(icT.N) : icT.N == null);
+            }
+            return false;
+        }
+
+        public String toString() {
+            return C.toString();
+        }
+
+        public int compareTo(Object arg0) {
+            return C.compareTo(((IcTuple)arg0).C);
+        }
+
+        public boolean isAnonymous() {
+            String className = C.toString();
+            String innerName = className.substring(className.lastIndexOf('$') + 1);
+            return Character.isDigit(innerName.charAt(0));
+        }
+
+    }
+
+    public IcTuple getIcTuple(CPClass inner) {
+        for (Iterator iterator = innerClasses.iterator(); iterator.hasNext();) {
+            IcTuple icTuple = (IcTuple) iterator.next();
+            if(icTuple.C.equals(inner)) {
+                return icTuple;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/IntList.java b/src/main/java/org/apache/harmony/pack200/IntList.java
new file mode 100644
index 0000000..efa8e1c
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/IntList.java
@@ -0,0 +1,260 @@
+/*
+ *  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.harmony.pack200;
+
+import java.util.Arrays;
+
+/**
+ * IntList is based on <code>java.util.ArrayList</code>, but is written
+ * specifically for ints in order to reduce boxing and unboxing to Integers,
+ * reduce the memory required and improve performance of pack200.
+ */
+public class IntList {
+
+    private int[] array;
+    private int firstIndex;
+    private int lastIndex;
+    private int modCount;
+
+    /**
+     * Constructs a new instance of IntList with capacity for ten elements.
+     */
+    public IntList() {
+        this(10);
+    }
+
+    /**
+     * Constructs a new instance of IntList with the specified capacity.
+     *
+     * @param capacity
+     *            the initial capacity of this IntList
+     */
+    public IntList(int capacity) {
+        if (capacity < 0) {
+            throw new IllegalArgumentException();
+        }
+        firstIndex = lastIndex = 0;
+        array = new int[capacity];
+    }
+
+    /**
+     * Adds the specified object at the end of this IntList.
+     *
+     * @param object
+     *            the object to add
+     * @return true
+     */
+    public boolean add(int object) {
+        if (lastIndex == array.length) {
+            growAtEnd(1);
+        }
+        array[lastIndex++] = object;
+        modCount++;
+        return true;
+    }
+
+    public void add(int location, int object) {
+        int size = lastIndex - firstIndex;
+        if (0 < location && location < size) {
+            if (firstIndex == 0 && lastIndex == array.length) {
+                growForInsert(location, 1);
+            } else if ((location < size / 2 && firstIndex > 0)
+                    || lastIndex == array.length) {
+                System.arraycopy(array, firstIndex, array, --firstIndex,
+                        location);
+            } else {
+                int index = location + firstIndex;
+                System.arraycopy(array, index, array, index + 1, size
+                        - location);
+                lastIndex++;
+            }
+            array[location + firstIndex] = object;
+        } else if (location == 0) {
+            if (firstIndex == 0) {
+                growAtFront(1);
+            }
+            array[--firstIndex] = object;
+        } else if (location == size) {
+            if (lastIndex == array.length) {
+                growAtEnd(1);
+            }
+            array[lastIndex++] = object;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+
+        modCount++;
+    }
+
+    public void clear() {
+        if (firstIndex != lastIndex) {
+            Arrays.fill(array, firstIndex, lastIndex, -1);
+            firstIndex = lastIndex = 0;
+            modCount++;
+        }
+    }
+
+    public int get(int location) {
+        if (0 <= location && location < (lastIndex - firstIndex)) {
+            return array[firstIndex + location];
+        }
+        throw new IndexOutOfBoundsException("" + location);
+    }
+
+    private void growAtEnd(int required) {
+        int size = lastIndex - firstIndex;
+        if (firstIndex >= required - (array.length - lastIndex)) {
+            int newLast = lastIndex - firstIndex;
+            if (size > 0) {
+                System.arraycopy(array, firstIndex, array, 0, size);
+            }
+            firstIndex = 0;
+            lastIndex = newLast;
+        } else {
+            int increment = size / 2;
+            if (required > increment) {
+                increment = required;
+            }
+            if (increment < 12) {
+                increment = 12;
+            }
+            int[] newArray = new int[size + increment];
+            if (size > 0) {
+                System.arraycopy(array, firstIndex, newArray, 0, size);
+                firstIndex = 0;
+                lastIndex = size;
+            }
+            array = newArray;
+        }
+    }
+
+    private void growAtFront(int required) {
+        int size = lastIndex - firstIndex;
+        if (array.length - lastIndex + firstIndex >= required) {
+            int newFirst = array.length - size;
+            if (size > 0) {
+                System.arraycopy(array, firstIndex, array, newFirst, size);
+            }
+            firstIndex = newFirst;
+            lastIndex = array.length;
+        } else {
+            int increment = size / 2;
+            if (required > increment) {
+                increment = required;
+            }
+            if (increment < 12) {
+                increment = 12;
+            }
+            int[] newArray = new int[size + increment];
+            if (size > 0) {
+                System.arraycopy(array, firstIndex, newArray, newArray.length
+                        - size, size);
+            }
+            firstIndex = newArray.length - size;
+            lastIndex = newArray.length;
+            array = newArray;
+        }
+    }
+
+    private void growForInsert(int location, int required) {
+        int size = lastIndex - firstIndex;
+        int increment = size / 2;
+        if (required > increment) {
+            increment = required;
+        }
+        if (increment < 12) {
+            increment = 12;
+        }
+        int[] newArray = new int[size + increment];
+        int newFirst = increment - required;
+        // Copy elements after location to the new array skipping inserted
+        // elements
+        System.arraycopy(array, location + firstIndex, newArray, newFirst
+                + location + required, size - location);
+        // Copy elements before location to the new array from firstIndex
+        System.arraycopy(array, firstIndex, newArray, newFirst, location);
+        firstIndex = newFirst;
+        lastIndex = size + increment;
+
+        array = newArray;
+    }
+
+    public void increment(int location) {
+        if (0 <= location && location < (lastIndex - firstIndex)) {
+            array[firstIndex + location]++;
+        } else {
+            throw new IndexOutOfBoundsException("" + location);
+        }
+    }
+
+    public boolean isEmpty() {
+        return lastIndex == firstIndex;
+    }
+
+    public int remove(int location) {
+        int result;
+        int size = lastIndex - firstIndex;
+        if (0 <= location && location < size) {
+            if (location == size - 1) {
+                result = array[--lastIndex];
+                array[lastIndex] = 0;
+            } else if (location == 0) {
+                result = array[firstIndex];
+                array[firstIndex++] = 0;
+            } else {
+                int elementIndex = firstIndex + location;
+                result = array[elementIndex];
+                if (location < size / 2) {
+                    System.arraycopy(array, firstIndex, array, firstIndex + 1,
+                            location);
+                    array[firstIndex++] = 0;
+                } else {
+                    System.arraycopy(array, elementIndex + 1, array,
+                            elementIndex, size - location - 1);
+                    array[--lastIndex] = 0;
+                }
+            }
+            if (firstIndex == lastIndex) {
+                firstIndex = lastIndex = 0;
+            }
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+
+        modCount++;
+        return result;
+    }
+
+    public int size() {
+        return lastIndex - firstIndex;
+    }
+
+    public int[] toArray() {
+        int size = lastIndex - firstIndex;
+        int[] result = new int[size];
+        System.arraycopy(array, firstIndex, result, 0, size);
+        return result;
+    }
+
+    public void addAll(IntList list) {
+        growAtEnd(list.size());
+        for (int i = 0; i < list.size(); i++) {
+            add(list.get(i));
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java b/src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java
new file mode 100644
index 0000000..1719396
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/MetadataBandGroup.java
@@ -0,0 +1,462 @@
+/*
+ * 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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A group of metadata (annotation) bands, such as class_RVA_bands,
+ * method_AD_bands etc.
+ */
+public class MetadataBandGroup extends BandSet {
+
+    public static final int CONTEXT_CLASS = 0;
+    public static final int CONTEXT_FIELD = 1;
+    public static final int CONTEXT_METHOD = 2;
+
+    private final String type;
+    private int numBackwardsCalls = 0;
+
+    public IntList param_NB = new IntList(); // TODO: Lazy instantiation?
+    public IntList anno_N = new IntList();
+    public List type_RS = new ArrayList();
+    public IntList pair_N = new IntList();
+    public List name_RU = new ArrayList();
+    public List T = new ArrayList();
+    public List caseI_KI = new ArrayList();
+    public List caseD_KD = new ArrayList();
+    public List caseF_KF = new ArrayList();
+    public List caseJ_KJ = new ArrayList();
+    public List casec_RS = new ArrayList();
+    public List caseet_RS = new ArrayList();
+    public List caseec_RU = new ArrayList();
+    public List cases_RU = new ArrayList();
+    public IntList casearray_N = new IntList();
+    public List nesttype_RS = new ArrayList();
+    public IntList nestpair_N = new IntList();
+    public List nestname_RU = new ArrayList();
+
+    private final CpBands cpBands;
+    private final int context;
+
+    /**
+     * Constructs a new MetadataBandGroup
+     *
+     * @param type
+     *            must be either AD, RVA, RIA, RVPA or RIPA.
+     * @param context
+     *            <code>CONTEXT_CLASS</code>, <code>CONTEXT_METHOD</code> or
+     *            <code>CONTEXT_FIELD</code>
+     * @param cpBands
+     *            constant pool bands
+     * @param segmentHeader
+     *            segment header
+     * @param effort
+     *            packing effort
+     */
+    public MetadataBandGroup(String type, int context, CpBands cpBands, SegmentHeader segmentHeader, int effort) {
+        super(effort, segmentHeader);
+        this.type = type;
+        this.cpBands = cpBands;
+        this.context = context;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.harmony.pack200.BandSet#pack(java.io.OutputStream)
+     */
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        PackingUtils.log("Writing metadata band group...");
+        if(hasContent()) {
+            String contextStr;
+            if(context == CONTEXT_CLASS) {
+                contextStr = "Class";
+            } else if (context == CONTEXT_FIELD) {
+                contextStr = "Field";
+            } else {
+                contextStr = "Method";
+            }
+            byte[] encodedBand = null;
+            if(!type.equals("AD")) {
+                if(type.indexOf('P') != -1) {
+                    // Parameter annotation so we need to transmit param_NB
+                    encodedBand = encodeBandInt(
+                            contextStr + "_" + type + " param_NB", param_NB.toArray(),
+                            Codec.BYTE1);
+                    out.write(encodedBand);
+                    PackingUtils.log("Wrote " + encodedBand.length
+                            + " bytes from " + contextStr + "_" + type + " anno_N["
+                            + param_NB.size() + "]");
+                }
+                encodedBand = encodeBandInt(
+                        contextStr + "_" + type + " anno_N", anno_N.toArray(),
+                        Codec.UNSIGNED5);
+                out.write(encodedBand);
+                PackingUtils.log("Wrote " + encodedBand.length
+                        + " bytes from " + contextStr + "_" + type + " anno_N["
+                        + anno_N.size() + "]");
+
+                encodedBand = encodeBandInt(contextStr + "_" + type
+                        + " type_RS", cpEntryListToArray(type_RS),
+                        Codec.UNSIGNED5);
+                out.write(encodedBand);
+                PackingUtils.log("Wrote " + encodedBand.length
+                        + " bytes from " + contextStr + "_" + type
+                        + " type_RS[" + type_RS.size() + "]");
+
+                encodedBand = encodeBandInt(
+                        contextStr + "_" + type + " pair_N", pair_N.toArray(),
+                        Codec.UNSIGNED5);
+                out.write(encodedBand);
+                PackingUtils.log("Wrote " + encodedBand.length
+                        + " bytes from " + contextStr + "_" + type + " pair_N["
+                        + pair_N.size() + "]");
+
+                encodedBand = encodeBandInt(contextStr + "_" + type
+                        + " name_RU", cpEntryListToArray(name_RU),
+                        Codec.UNSIGNED5);
+                out.write(encodedBand);
+                PackingUtils.log("Wrote " + encodedBand.length
+                        + " bytes from " + contextStr + "_" + type
+                        + " name_RU[" + name_RU.size() + "]");
+            }
+            encodedBand = encodeBandInt(contextStr + "_" + type + " T",
+                    tagListToArray(T), Codec.BYTE1);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " T[" + T.size() + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI",
+                    cpEntryListToArray(caseI_KI), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " caseI_KI[" + caseI_KI.size()
+                    + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD",
+                    cpEntryListToArray(caseD_KD), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " caseD_KD[" + caseD_KD.size()
+                    + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF",
+                    cpEntryListToArray(caseF_KF), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " caseF_KF[" + caseF_KF.size()
+                    + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ",
+                    cpEntryListToArray(caseJ_KJ), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " caseJ_KJ[" + caseJ_KJ.size()
+                    + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS",
+                    cpEntryListToArray(casec_RS), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " casec_RS[" + casec_RS.size()
+                    + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS",
+                    cpEntryListToArray(caseet_RS), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " caseet_RS["
+                    + caseet_RS.size() + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU",
+                    cpEntryListToArray(caseec_RU), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " caseec_RU["
+                    + caseec_RU.size() + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU",
+                    cpEntryListToArray(cases_RU), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " cases_RU[" + cases_RU.size()
+                    + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type
+                    + " casearray_N", casearray_N.toArray(), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " casearray_N["
+                    + casearray_N.size() + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type
+                    + " nesttype_RS", cpEntryListToArray(nesttype_RS),
+                    Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " nesttype_RS["
+                    + nesttype_RS.size() + "]");
+
+            encodedBand = encodeBandInt(
+                    contextStr + "_" + type + " nestpair_N", nestpair_N
+                            .toArray(), Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " nestpair_N["
+                    + nestpair_N.size() + "]");
+
+            encodedBand = encodeBandInt(contextStr + "_" + type
+                    + " nestname_RU", cpEntryListToArray(nestname_RU),
+                    Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + contextStr + "_" + type + " nestname_RU["
+                    + nestname_RU.size() + "]");
+        }
+    }
+
+    private int[] tagListToArray(List t2) {
+        int[] ints = new int[t2.size()];
+        for (int i = 0; i < ints.length; i++) {
+            ints[i] = ((String)t2.get(i)).charAt(0);
+        }
+        return ints;
+    }
+
+    /**
+     * Add an annotation to this set of bands
+     *
+     * @param desc
+     * @param nameRU
+     * @param t
+     * @param values
+     * @param caseArrayN
+     * @param nestTypeRS
+     * @param nestNameRU
+     * @param nestPairN
+     */
+    public void addParameterAnnotation(int numParams, int[] annoN, IntList pairN, List typeRS, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+        param_NB.add(numParams);
+        for (int i = 0; i < annoN.length; i++) {
+            anno_N.add(annoN[i]);
+        }
+        pair_N.addAll(pairN);
+        for (Iterator iterator = typeRS.iterator(); iterator.hasNext();) {
+            String desc = (String) iterator.next();
+            type_RS.add(cpBands.getCPSignature(desc));
+        }
+        for (Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
+            String name = (String) iterator.next();
+            name_RU.add(cpBands.getCPUtf8(name));
+        }
+        Iterator valuesIterator = values.iterator();
+        for (Iterator iterator = t.iterator(); iterator.hasNext();) {
+            String tag = (String) iterator.next();
+            T.add(tag);
+            if (tag.equals("B") || tag.equals("C") || tag.equals("I")
+                    || tag.equals("S") || tag.equals("Z")) {
+                Integer value = (Integer)valuesIterator.next();
+                caseI_KI.add(cpBands.getConstant(value));
+            } else if (tag.equals("D")) {
+                Double value = (Double)valuesIterator.next();
+                caseD_KD.add(cpBands.getConstant(value));
+            } else if (tag.equals("F")) {
+                Float value = (Float)valuesIterator.next();
+                caseF_KF.add(cpBands.getConstant(value));
+            } else if (tag.equals("J")) {
+                Long value = (Long)valuesIterator.next();
+                caseJ_KJ.add(cpBands.getConstant(value));
+            } else if (tag.equals("c")) {
+                String value = (String)valuesIterator.next();
+                casec_RS.add(cpBands.getCPSignature(value));
+            } else if (tag.equals("e")) {
+                String value = (String)valuesIterator.next();
+                String value2 = (String)valuesIterator.next();
+                caseet_RS.add(cpBands.getCPSignature(value));
+                caseec_RU.add(cpBands.getCPUtf8(value2));
+            } else if (tag.equals("s")) {
+                String value = (String)valuesIterator.next();
+                cases_RU.add(cpBands.getCPUtf8(value));
+            }
+            // do nothing here for [ or @ (handled below)
+        }
+        for (Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
+            int arraySize = ((Integer)iterator.next()).intValue();
+            casearray_N.add(arraySize);
+            numBackwardsCalls += arraySize;
+        }
+        for (Iterator iterator = nestTypeRS.iterator(); iterator.hasNext();) {
+            String type = (String) iterator.next();
+            nesttype_RS.add(cpBands.getCPSignature(type));
+        }
+        for (Iterator iterator = nestNameRU.iterator(); iterator.hasNext();) {
+            String name = (String) iterator.next();
+            nestname_RU.add(cpBands.getCPUtf8(name));
+        }
+        for (Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
+            Integer numPairs = (Integer) iterator.next();
+            nestpair_N.add(numPairs.intValue());
+            numBackwardsCalls += numPairs.intValue();
+        }
+    }
+
+    /**
+     * Add an annotation to this set of bands
+     *
+     * @param desc
+     * @param nameRU
+     * @param t
+     * @param values
+     * @param caseArrayN
+     * @param nestTypeRS
+     * @param nestNameRU
+     * @param nestPairN
+     */
+    public void addAnnotation(String desc, List nameRU, List t, List values, List caseArrayN, List nestTypeRS, List nestNameRU, List nestPairN) {
+        type_RS.add(cpBands.getCPSignature(desc));
+        pair_N.add(nameRU.size());
+
+        for (Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
+            String name = (String) iterator.next();
+            name_RU.add(cpBands.getCPUtf8(name));
+        }
+
+        Iterator valuesIterator = values.iterator();
+        for (Iterator iterator = t.iterator(); iterator.hasNext();) {
+            String tag = (String) iterator.next();
+            T.add(tag);
+            if (tag.equals("B") || tag.equals("C") || tag.equals("I")
+                    || tag.equals("S") || tag.equals("Z")) {
+                Integer value = (Integer)valuesIterator.next();
+                caseI_KI.add(cpBands.getConstant(value));
+            } else if (tag.equals("D")) {
+                Double value = (Double)valuesIterator.next();
+                caseD_KD.add(cpBands.getConstant(value));
+            } else if (tag.equals("F")) {
+                Float value = (Float)valuesIterator.next();
+                caseF_KF.add(cpBands.getConstant(value));
+            } else if (tag.equals("J")) {
+                Long value = (Long)valuesIterator.next();
+                caseJ_KJ.add(cpBands.getConstant(value));
+            } else if (tag.equals("c")) {
+                String value = (String)valuesIterator.next();
+                casec_RS.add(cpBands.getCPSignature(value));
+            } else if (tag.equals("e")) {
+                String value = (String)valuesIterator.next();
+                String value2 = (String)valuesIterator.next();
+                caseet_RS.add(cpBands.getCPSignature(value));
+                caseec_RU.add(cpBands.getCPUtf8(value2));
+            } else if (tag.equals("s")) {
+                String value = (String)valuesIterator.next();
+                cases_RU.add(cpBands.getCPUtf8(value));
+            }
+            // do nothing here for [ or @ (handled below)
+        }
+        for (Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
+            int arraySize = ((Integer)iterator.next()).intValue();
+            casearray_N.add(arraySize);
+            numBackwardsCalls += arraySize;
+        }
+        for (Iterator iterator = nestTypeRS.iterator(); iterator.hasNext();) {
+            String type = (String) iterator.next();
+            nesttype_RS.add(cpBands.getCPSignature(type));
+        }
+        for (Iterator iterator = nestNameRU.iterator(); iterator.hasNext();) {
+            String name = (String) iterator.next();
+            nestname_RU.add(cpBands.getCPUtf8(name));
+        }
+        for (Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
+            Integer numPairs = (Integer) iterator.next();
+            nestpair_N.add(numPairs.intValue());
+            numBackwardsCalls += numPairs.intValue();
+        }
+    }
+
+    /**
+     * Returns true if any annotations have been added to this set of bands
+     */
+    public boolean hasContent() {
+        return type_RS.size() > 0;
+    }
+
+    public int numBackwardsCalls() {
+        return numBackwardsCalls;
+    }
+
+    public void incrementAnnoN() {
+        anno_N.increment(anno_N.size() - 1);
+    }
+
+    public void newEntryInAnnoN() {
+        anno_N.add(1);
+    }
+
+    /**
+     * Remove the latest annotation that was added to this group
+     */
+    public void removeLatest() {
+        int latest = anno_N.remove(anno_N.size() -1);
+        for (int i = 0; i < latest; i++) {
+            type_RS.remove(type_RS.size() - 1);
+            int pairs = pair_N.remove(pair_N.size() - 1);
+            for (int j = 0; j < pairs; j++) {
+                removeOnePair();
+            }
+        }
+    }
+
+    /*
+     * Convenience method for removeLatest
+     */
+    private void removeOnePair() {
+        String tag = (String) T.remove(T.size() - 1);
+        if (tag.equals("B") || tag.equals("C") || tag.equals("I")
+                || tag.equals("S") || tag.equals("Z")) {
+            caseI_KI.remove(caseI_KI.size() - 1);
+        } else if (tag.equals("D")) {
+            caseD_KD.remove(caseD_KD.size() - 1);
+        } else if (tag.equals("F")) {
+            caseF_KF.remove(caseF_KF.size() - 1);
+        } else if (tag.equals("J")) {
+            caseJ_KJ.remove(caseJ_KJ.size() - 1);
+        } else if (tag.equals("C")) {
+            casec_RS.remove(casec_RS.size() - 1);
+        } else if (tag.equals("e")) {
+            caseet_RS.remove(caseet_RS.size() - 1);
+            caseec_RU.remove(caseet_RS.size() - 1);
+        } else if (tag.equals("s")) {
+            cases_RU.remove(cases_RU.size() - 1);
+        } else if (tag.equals("[")) {
+            int arraySize = casearray_N.remove(casearray_N.size() - 1);
+            numBackwardsCalls -= arraySize;
+            for (int k = 0; k < arraySize; k++) {
+                removeOnePair();
+            }
+        } else if (tag.equals("@")) {
+            nesttype_RS.remove(nesttype_RS.size() - 1);
+            int numPairs = nestpair_N.remove(nestpair_N.size() - 1);
+            numBackwardsCalls -= numPairs;
+            for (int i = 0; i < numPairs; i++) {
+                removeOnePair();
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/NewAttribute.java b/src/main/java/org/apache/harmony/pack200/NewAttribute.java
new file mode 100644
index 0000000..54b867b
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/NewAttribute.java
@@ -0,0 +1,202 @@
+/*
+ *  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.harmony.pack200;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+
+/**
+ * NewAttribute extends <code>Attribute</code> and manages unknown attributes
+ * encountered by ASM that have had a layout definition given to pack200 (e.g.
+ * via one of the -C, -M, -F or -D command line options)
+ */
+public class NewAttribute extends Attribute {
+
+    private boolean contextClass = false;
+    private boolean contextMethod = false;
+    private boolean contextField = false;
+    private boolean contextCode = false;
+
+    private final String layout;
+    private byte[] contents;
+    private int codeOff;
+    private Label[] labels;
+    private ClassReader classReader;
+    private char[] buf;
+
+    public NewAttribute(String type, String layout, int context) {
+        super(type);
+        this.layout = layout;
+        addContext(context);
+    }
+
+    public NewAttribute(ClassReader classReader, String type, String layout, byte[] contents, char[] buf,
+            int codeOff, Label[] labels) {
+        super(type);
+        this.classReader = classReader;
+        this.contents = contents;
+        this.layout = layout;
+        this.codeOff = codeOff;
+        this.labels = labels;
+        this.buf = buf;
+    }
+
+    public void addContext(int context) {
+        switch(context) {
+        case AttributeDefinitionBands.CONTEXT_CLASS:
+            contextClass = true;
+            break;
+        case AttributeDefinitionBands.CONTEXT_METHOD:
+            contextMethod = true;
+            break;
+        case AttributeDefinitionBands.CONTEXT_FIELD:
+            contextField = true;
+            break;
+        case AttributeDefinitionBands.CONTEXT_CODE:
+            contextCode = true;
+            break;
+        }
+    }
+
+    public boolean isContextClass() {
+        return contextClass;
+    }
+
+    public boolean isContextMethod() {
+        return contextMethod;
+    }
+
+    public boolean isContextField() {
+        return contextField;
+    }
+
+    public boolean isContextCode() {
+        return contextCode;
+    }
+
+    public String getLayout() {
+        return layout;
+    }
+
+    public boolean isUnknown() {
+        return false;
+    }
+
+    public boolean isCodeAttribute() {
+        return codeOff != -1;
+    }
+
+    protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+            int codeOff, Label[] labels) {
+        byte[] attributeContents = new byte[len];
+        System.arraycopy(cr.b, off, attributeContents, 0, len);
+        return new NewAttribute(cr, type, layout, attributeContents, buf, codeOff,
+                labels);
+    }
+
+    public boolean isUnknown(int context) {
+        switch(context) {
+        case AttributeDefinitionBands.CONTEXT_CLASS:
+            return !contextClass;
+        case AttributeDefinitionBands.CONTEXT_METHOD:
+            return !contextMethod;
+        case AttributeDefinitionBands.CONTEXT_FIELD:
+            return !contextField;
+        case AttributeDefinitionBands.CONTEXT_CODE:
+            return !contextCode;
+        }
+        return false;
+    }
+
+    public String readUTF8(int index) {
+        return classReader.readUTF8(index, buf);
+    }
+
+    public String readClass(int index) {
+        return classReader.readClass(index, buf);
+    }
+
+    public Object readConst(int index) {
+        return classReader.readConst(index, buf);
+    }
+
+    public byte[] getBytes() {
+        return contents;
+    }
+
+    public Label getLabel(int index) {
+        return labels[index];
+    }
+
+    /**
+     * ErrorAttribute extends <code>NewAttribute</code> and manages attributes
+     * encountered by ASM that have had an error action specified to pack200
+     * (e.g. via one of the -C, -M, -F or -D command line options such as
+     * -Cattribute-name=error)
+     */
+    public static class ErrorAttribute extends NewAttribute {
+
+        public ErrorAttribute(String type, int context) {
+            super(type, "", context);
+        }
+
+        protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+                int codeOff, Label[] labels) {
+            throw new Error("Attribute " + type + " was found");
+        }
+
+    }
+
+    /**
+     * StripAttribute extends <code>NewAttribute</code> and manages attributes
+     * encountered by ASM that have had an strip action specified to pack200
+     * (e.g. via one of the -C, -M, -F or -D command line options such as
+     * -Cattribute-name=strip)
+     */
+    public static class StripAttribute extends NewAttribute {
+
+        public StripAttribute(String type, int context) {
+            super(type, "", context);
+        }
+
+        protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+                int codeOff, Label[] labels) {
+            // TODO Not sure if this works, can we really strip an attribute if we don't know the layout?
+            return null;
+        }
+    }
+
+    /**
+     * PassAttribute extends <code>NewAttribute</code> and manages attributes
+     * encountered by ASM that have had an pass action specified to pack200
+     * (e.g. via one of the -C, -M, -F or -D command line options such as
+     * -Cattribute-name=pass)
+     */
+    public static class PassAttribute extends NewAttribute {
+
+        public PassAttribute(String type, int context) {
+            super(type, "", context);
+        }
+
+        protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+                int codeOff, Label[] labels) {
+            throw new Segment.PassException();
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/harmony/pack200/NewAttributeBands.java b/src/main/java/org/apache/harmony/pack200/NewAttributeBands.java
new file mode 100644
index 0000000..fcbd1b7
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/NewAttributeBands.java
@@ -0,0 +1,965 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
+import org.objectweb.asm.Label;
+
+/**
+ * Set of bands relating to a non-predefined attribute that has had a layout
+ * definition given to pack200 (e.g. via one of the -C, -M, -F or -D command
+ * line options)
+ */
+public class NewAttributeBands extends BandSet {
+
+    protected List attributeLayoutElements;
+    private int[] backwardsCallCounts;
+    private final CpBands cpBands;
+    private final AttributeDefinition def;
+    private boolean usedAtLeastOnce;
+
+    // used when parsing
+    private Integral lastPIntegral;
+
+    public NewAttributeBands(int effort, CpBands cpBands, SegmentHeader header, AttributeDefinition def) throws IOException {
+        super(effort, header);
+        this.def = def;
+        this.cpBands = cpBands;
+        parseLayout();
+    }
+
+    public void addAttribute(NewAttribute attribute) {
+        usedAtLeastOnce = true;
+        InputStream stream = new ByteArrayInputStream(attribute.getBytes());
+        for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
+            AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+            layoutElement.addAttributeToBand(attribute, stream);
+        }
+    }
+
+    public void pack(OutputStream out) throws IOException, Pack200Exception {
+        for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
+            AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+            layoutElement.pack(out);
+        }
+    }
+
+    public String getAttributeName() {
+        return def.name.getUnderlyingString();
+    }
+
+    public int getFlagIndex() {
+        return def.index;
+    }
+
+    public int[] numBackwardsCalls() {
+        return backwardsCallCounts;
+    }
+
+    public boolean isUsedAtLeastOnce() {
+        return usedAtLeastOnce;
+    }
+
+    private void parseLayout() throws IOException {
+        String layout = def.layout.getUnderlyingString();
+        if (attributeLayoutElements == null) {
+            attributeLayoutElements = new ArrayList();
+            StringReader stream = new StringReader(layout);
+            AttributeLayoutElement e;
+            while ((e = readNextAttributeElement(stream)) != null) {
+                attributeLayoutElements.add(e);
+            }
+            resolveCalls();
+        }
+    }
+
+    /**
+     * Resolve calls in the attribute layout and returns the number of backwards
+     * callables
+     *
+     * @param tokens -
+     *            the attribute layout as a List of AttributeElements
+     */
+    private void resolveCalls() {
+        for (int i = 0; i < attributeLayoutElements.size(); i++) {
+            AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
+                    .get(i);
+            if (element instanceof Callable) {
+                Callable callable = (Callable) element;
+                List body = callable.body; // Look for calls in the body
+                for (int iIndex = 0; iIndex < body.size(); iIndex++) {
+                    LayoutElement layoutElement = (LayoutElement) body
+                            .get(iIndex);
+                    // Set the callable for each call
+                    resolveCallsForElement(i, callable, layoutElement);
+                }
+            }
+        }
+        int backwardsCallableIndex = 0;
+        for (int i = 0; i < attributeLayoutElements.size(); i++) {
+            AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
+                    .get(i);
+            if (element instanceof Callable) {
+                Callable callable = (Callable) element;
+                if(callable.isBackwardsCallable) {
+                    callable.setBackwardsCallableIndex(backwardsCallableIndex);
+                    backwardsCallableIndex++;
+                }
+            }
+        }
+        backwardsCallCounts = new int[backwardsCallableIndex];
+    }
+
+    private void resolveCallsForElement(int i,
+            Callable currentCallable, LayoutElement layoutElement) {
+        if (layoutElement instanceof Call) {
+            Call call = (Call) layoutElement;
+            int index = call.callableIndex;
+            if (index == 0) { // Calls the parent callable
+                call.setCallable(currentCallable);
+            } else if (index > 0) { // Forwards call
+                for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
+                    AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
+                            .get(k);
+                    if (el instanceof Callable) {
+                        index--;
+                        if (index == 0) {
+                            call.setCallable((Callable) el);
+                            break;
+                        }
+                    }
+                }
+            } else { // Backwards call
+                for (int k = i - 1; k >= 0; k--) {
+                    AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
+                            .get(k);
+                    if (el instanceof Callable) {
+                        index++;
+                        if (index == 0) {
+                            call.setCallable((Callable) el);
+                            break;
+                        }
+                    }
+                }
+            }
+        } else if (layoutElement instanceof Replication) {
+            List children = ((Replication)layoutElement).layoutElements;
+            for (Iterator iterator = children.iterator(); iterator.hasNext();) {
+                LayoutElement object = (LayoutElement) iterator.next();
+                resolveCallsForElement(i, currentCallable, object);
+            }
+        }
+    }
+
+    private AttributeLayoutElement readNextAttributeElement(StringReader stream)
+            throws IOException {
+        stream.mark(1);
+        int nextChar = stream.read();
+        if (nextChar == -1) {
+            return null;
+        }
+        if (nextChar == '[') {
+            List body = readBody(getStreamUpToMatchingBracket(stream));
+            return new Callable(body);
+        } else {
+            stream.reset();
+            return readNextLayoutElement(stream);
+        }
+    }
+
+    private LayoutElement readNextLayoutElement(StringReader stream)
+            throws IOException {
+        int nextChar = stream.read();
+        if (nextChar == -1) {
+            return null;
+        }
+
+        switch (nextChar) {
+        // Integrals
+        case 'B':
+        case 'H':
+        case 'I':
+        case 'V':
+            return new Integral(new String(new char[] { (char)nextChar }));
+        case 'S':
+        case 'F':
+            return new Integral(new String(new char[] { (char)nextChar,
+                    (char) stream.read() }));
+        case 'P':
+            stream.mark(1);
+            if (stream.read() != 'O') {
+                stream.reset();
+                lastPIntegral = new Integral("P" + (char) stream.read());
+                return lastPIntegral;
+            } else {
+                lastPIntegral = new Integral("PO" + (char) stream.read(), lastPIntegral);
+                return lastPIntegral;
+            }
+        case 'O':
+            stream.mark(1);
+            if (stream.read() != 'S') {
+                stream.reset();
+                return new Integral("O" + (char) stream.read(), lastPIntegral);
+            } else {
+                return new Integral("OS" + (char) stream.read(), lastPIntegral);
+            }
+
+            // Replication
+        case 'N':
+            char uint_type = (char) stream.read();
+            stream.read(); // '['
+            String str = readUpToMatchingBracket(stream);
+            return new Replication("" + uint_type, str);
+
+            // Union
+        case 'T':
+            String int_type = "" + (char) stream.read();
+            if (int_type.equals("S")) {
+                int_type += (char) stream.read();
+            }
+            List unionCases = new ArrayList();
+            UnionCase c;
+            while ((c = readNextUnionCase(stream)) != null) {
+                unionCases.add(c);
+            }
+            stream.read(); // '('
+            stream.read(); // ')'
+            stream.read(); // '['
+            List body = null;
+            stream.mark(1);
+            char next = (char) stream.read();
+            if (next != ']') {
+                stream.reset();
+                body = readBody(getStreamUpToMatchingBracket(stream));
+            }
+            return new Union(int_type, unionCases, body);
+
+            // Call
+        case '(':
+            int number = readNumber(stream).intValue();
+            stream.read(); // ')'
+            return new Call(number);
+            // Reference
+        case 'K':
+        case 'R':
+            String string = "" + (char)nextChar + (char) stream.read();
+            char nxt = (char) stream.read();
+            string += nxt;
+            if (nxt == 'N') {
+                string += (char) stream.read();
+            }
+            return new Reference(string);
+        }
+        return null;
+    }
+
+    /**
+     * Read a UnionCase from the stream
+     *
+     * @param stream
+     * @return
+     * @throws IOException
+     */
+    private UnionCase readNextUnionCase(StringReader stream) throws IOException {
+        stream.mark(2);
+        stream.read(); // '('
+        char next = (char) stream.read();
+        if (next == ')') {
+            stream.reset();
+            return null;
+        } else {
+            stream.reset();
+            stream.read(); // '('
+        }
+        List tags = new ArrayList();
+        Integer nextTag;
+        do {
+            nextTag = readNumber(stream);
+            if(nextTag != null) {
+                tags.add(nextTag);
+                stream.read(); // ',' or ')'
+            }
+        } while (nextTag != null);
+        stream.read(); // '['
+        stream.mark(1);
+        next = (char) stream.read();
+        if (next == ']') {
+            return new UnionCase(tags);
+        } else {
+            stream.reset();
+            return new UnionCase(tags,
+                    readBody(getStreamUpToMatchingBracket(stream)));
+        }
+    }
+
+    /**
+     * An AttributeLayoutElement is a part of an attribute layout and has one or
+     * more bands associated with it, which transmit the AttributeElement data
+     * for successive Attributes of this type.
+     */
+    public interface AttributeLayoutElement {
+
+        public void addAttributeToBand(NewAttribute attribute, InputStream stream);
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception;
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets);
+
+    }
+
+    public abstract class LayoutElement implements AttributeLayoutElement {
+
+        protected int getLength(char uint_type) {
+            int length = 0;
+            switch (uint_type) {
+            case 'B':
+                length = 1;
+                break;
+            case 'H':
+                length = 2;
+                break;
+            case 'I':
+                length = 4;
+                break;
+            case 'V':
+                length = 0;
+                break;
+            }
+            return length;
+        }
+    }
+
+    public class Integral extends LayoutElement {
+
+        private final String tag;
+
+        private final List band = new ArrayList();
+        private final BHSDCodec defaultCodec;
+
+        // used for bytecode offsets (OH and POH)
+        private Integral previousIntegral;
+        private int previousPValue;
+
+        public Integral(String tag) {
+            this.tag = tag;
+            this.defaultCodec = getCodec(tag);
+        }
+
+        public Integral(String tag, Integral previousIntegral) {
+            this.tag = tag;
+            this.defaultCodec = getCodec(tag);
+            this.previousIntegral = previousIntegral;
+        }
+
+        public String getTag() {
+            return tag;
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            Object val = null;
+            int value = 0;
+            if (tag.equals("B") || tag.equals("FB")) {
+                value = readInteger(1, stream) & 0xFF; // unsigned byte
+            } else if (tag.equals("SB")) {
+                value = readInteger(1, stream);
+            } else if (tag.equals("H") || tag.equals("FH")) {
+                value = readInteger(2, stream) & 0xFFFF; // unsigned short
+            } else if (tag.equals("SH")) {
+                value = readInteger(2, stream);
+            } else if (tag.equals("I") || tag.equals("FI")) {
+                value = readInteger(4, stream);
+            } else if (tag.equals("SI")) {
+                value = readInteger(4, stream);
+            } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) {
+                // Not currently supported
+            } else if (tag.startsWith("PO") || tag.startsWith("OS")) {
+                char uint_type = tag.substring(2).toCharArray()[0];
+                int length = getLength(uint_type);
+                value = readInteger(length, stream);
+                value += previousIntegral.previousPValue;
+                val = attribute.getLabel(value);
+                previousPValue = value;
+            } else if (tag.startsWith("P")) {
+                char uint_type = tag.substring(1).toCharArray()[0];
+                int length = getLength(uint_type);
+                value = readInteger(length, stream);
+                val = attribute.getLabel(value);
+                previousPValue = value;
+            } else if (tag.startsWith("O")) {
+                char uint_type = tag.substring(1).toCharArray()[0];
+                int length = getLength(uint_type);
+                value = readInteger(length, stream);
+                value += previousIntegral.previousPValue;
+                val = attribute.getLabel(value);
+                previousPValue = value;
+            }
+            if(val == null) {
+                val = new Integer(value);
+            }
+            band.add(val);
+        }
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception {
+            PackingUtils.log("Writing new attribute bands...");
+            byte[] encodedBand = encodeBandInt(tag, integerListToArray(band),
+                    defaultCodec);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + tag + "[" + band.size() + "]");
+        }
+
+        public int latestValue() {
+            return ((Integer)band.get(band.size() - 1)).intValue();
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            if(tag.startsWith("O") || tag.startsWith("PO")) {
+                renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets);
+            } else if (tag.startsWith("P")) {
+                for (int i = band.size() - 1; i >= 0; i--) {
+                    Object label = band.get(i);
+                    if (label instanceof Integer) {
+                        break;
+                    } else if (label instanceof Label) {
+                        band.remove(i);
+                        Integer bytecodeIndex = (Integer) labelsToOffsets
+                                .get(label);
+                        band.add(i, new Integer(bciRenumbering.get(bytecodeIndex
+                                .intValue())));
+                    }
+                }
+            }
+        }
+
+        private void renumberOffsetBci(List relative,
+                IntList bciRenumbering, Map labelsToOffsets) {
+            for (int i = band.size() - 1; i >= 0; i--) {
+                Object label = band.get(i);
+                if (label instanceof Integer) {
+                    break;
+                } else if (label instanceof Label) {
+                    band.remove(i);
+                    Integer bytecodeIndex = (Integer) labelsToOffsets.get(label);
+                    Integer renumberedOffset = new Integer(bciRenumbering
+                            .get(bytecodeIndex.intValue())
+                            - ((Integer) relative.get(i)).intValue());
+                    band.add(i, renumberedOffset);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * A replication is an array of layout elements, with an associated count
+     */
+    public class Replication extends LayoutElement {
+
+        private final Integral countElement;
+
+        private final List layoutElements = new ArrayList();
+
+        public Integral getCountElement() {
+            return countElement;
+        }
+
+        public List getLayoutElements() {
+            return layoutElements;
+        }
+
+        public Replication(String tag, String contents) throws IOException {
+            this.countElement = new Integral(tag);
+            StringReader stream = new StringReader(contents);
+            LayoutElement e;
+            while ((e = readNextLayoutElement(stream)) != null) {
+                layoutElements.add(e);
+            }
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            countElement.addAttributeToBand(attribute, stream);
+            int count = countElement.latestValue();
+            for (int i = 0; i < count; i++) {
+                for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
+                    AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+                    layoutElement.addAttributeToBand(attribute, stream);
+                }
+            }
+        }
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception {
+            countElement.pack(out);
+            for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+                layoutElement.pack(out);
+            }
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            for (Iterator iterator = layoutElements.iterator(); iterator.hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+                layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
+            }
+        }
+    }
+
+    /**
+     * A Union is a type of layout element where the tag value acts as a
+     * selector for one of the union cases
+     */
+    public class Union extends LayoutElement {
+
+        private final Integral unionTag;
+        private final List unionCases;
+        private final List defaultCaseBody;
+
+        public Union(String tag, List unionCases, List body) {
+            this.unionTag = new Integral(tag);
+            this.unionCases = unionCases;
+            this.defaultCaseBody = body;
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            unionTag.addAttributeToBand(attribute, stream);
+            long tag = unionTag.latestValue();
+            boolean defaultCase = true;
+            for (int i = 0; i < unionCases.size(); i++) {
+                UnionCase element = (UnionCase) unionCases.get(i);
+                if (element.hasTag(tag)) {
+                    defaultCase = false;
+                    element.addAttributeToBand(attribute, stream);
+                }
+            }
+            if (defaultCase) {
+                for (int i = 0; i < defaultCaseBody.size(); i++) {
+                    LayoutElement element = (LayoutElement) defaultCaseBody
+                            .get(i);
+                    element.addAttributeToBand(attribute, stream);
+                }
+            }
+        }
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception {
+            unionTag.pack(out);
+            for (Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
+                UnionCase unionCase = (UnionCase) iterator.next();
+                unionCase.pack(out);
+            }
+            for (Iterator iterator = defaultCaseBody.iterator(); iterator
+                    .hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator
+                        .next();
+                layoutElement.pack(out);
+            }
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            for (Iterator iterator = unionCases.iterator(); iterator.hasNext();) {
+                UnionCase unionCase = (UnionCase) iterator.next();
+                unionCase.renumberBci(bciRenumbering, labelsToOffsets);
+            }
+            for (Iterator iterator = defaultCaseBody.iterator(); iterator
+                    .hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator
+                        .next();
+                layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
+            }
+        }
+
+        public Integral getUnionTag() {
+            return unionTag;
+        }
+
+        public List getUnionCases() {
+            return unionCases;
+        }
+
+        public List getDefaultCaseBody() {
+            return defaultCaseBody;
+        }
+    }
+
+    public class Call extends LayoutElement {
+
+        private final int callableIndex;
+        private Callable callable;
+
+        public Call(int callableIndex) {
+            this.callableIndex = callableIndex;
+        }
+
+        public void setCallable(Callable callable) {
+            this.callable = callable;
+            if (callableIndex < 1) {
+                callable.setBackwardsCallable();
+            }
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            callable.addAttributeToBand(attribute, stream);
+            if(callableIndex < 1) {
+                callable.addBackwardsCall();
+            }
+        }
+
+        public void pack(OutputStream out) {
+            // do nothing here as pack will be called on the callable at another time
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            // do nothing here as renumberBci will be called on the callable at another time
+        }
+
+        public int getCallableIndex() {
+            return callableIndex;
+        }
+
+        public Callable getCallable() {
+            return callable;
+        }
+    }
+
+    /**
+     * Constant Pool Reference
+     */
+    public class Reference extends LayoutElement {
+
+        private final String tag;
+
+        private List band;
+
+        private boolean nullsAllowed = false;
+
+        public Reference(String tag) {
+            this.tag = tag;
+            nullsAllowed = tag.indexOf('N') != -1;
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            int index = readInteger(4, stream);
+            if(tag.startsWith("RC")) { // Class
+                band.add(cpBands.getCPClass(attribute.readClass(index)));
+            } else if (tag.startsWith("RU")) { // UTF8 String
+                band.add(cpBands.getCPUtf8(attribute.readUTF8(index)));
+            } else if (tag.startsWith("RS")) { // Signature
+                band.add(cpBands.getCPSignature(attribute.readUTF8(index)));
+            } else { // Constant
+                band.add(cpBands.getConstant(attribute.readConst(index)));
+            }
+            // TODO method and field references
+        }
+
+        public String getTag() {
+            return tag;
+        }
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception {
+            int[] ints;
+            if(nullsAllowed) {
+                ints = cpEntryOrNullListToArray(band);
+            } else {
+                ints = cpEntryListToArray(band);
+            }
+            byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5);
+            out.write(encodedBand);
+            PackingUtils.log("Wrote " + encodedBand.length + " bytes from "
+                    + tag + "[" + ints.length + "]");
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            // nothing to do here
+        }
+
+    }
+
+    public class Callable implements AttributeLayoutElement {
+
+        private final List body;
+
+        private boolean isBackwardsCallable;
+
+        private int backwardsCallableIndex;
+
+        public Callable(List body) throws IOException {
+            this.body = body;
+        }
+
+        public void setBackwardsCallableIndex(int backwardsCallableIndex) {
+            this.backwardsCallableIndex = backwardsCallableIndex;
+        }
+
+        public void addBackwardsCall() {
+            backwardsCallCounts[backwardsCallableIndex]++;
+        }
+
+        public boolean isBackwardsCallable() {
+            return isBackwardsCallable;
+        }
+
+        /**
+         * Tells this Callable that it is a backwards callable
+         */
+        public void setBackwardsCallable() {
+            this.isBackwardsCallable = true;
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            for (Iterator iterator = body.iterator(); iterator.hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+                layoutElement.addAttributeToBand(attribute, stream);
+            }
+        }
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception {
+            for (Iterator iterator = body.iterator(); iterator.hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+                layoutElement.pack(out);
+            }
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            for (Iterator iterator = body.iterator(); iterator.hasNext();) {
+                AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next();
+                layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
+            }
+        }
+
+        public List getBody() {
+            return body;
+        }
+    }
+
+    /**
+     * A Union case
+     */
+    public class UnionCase extends LayoutElement {
+
+        private final List body;
+
+        private final List tags;
+
+        public UnionCase(List tags) {
+            this.tags = tags;
+            this.body = Collections.EMPTY_LIST;
+        }
+
+        public boolean hasTag(long l) {
+            return tags.contains(new Integer((int) l));
+        }
+
+        public UnionCase(List tags, List body) throws IOException {
+            this.tags = tags;
+            this.body = body;
+        }
+
+        public void addAttributeToBand(NewAttribute attribute,
+                InputStream stream) {
+            for (int i = 0; i < body.size(); i++) {
+                LayoutElement element = (LayoutElement) body.get(i);
+                element.addAttributeToBand(attribute, stream);
+            }
+        }
+
+        public void pack(OutputStream out) throws IOException, Pack200Exception {
+            for (int i = 0; i < body.size(); i++) {
+                LayoutElement element = (LayoutElement) body.get(i);
+                element.pack(out);
+            }
+        }
+
+        public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+            for (int i = 0; i < body.size(); i++) {
+                LayoutElement element = (LayoutElement) body.get(i);
+                element.renumberBci(bciRenumbering, labelsToOffsets);
+            }
+        }
+
+        public List getBody() {
+            return body;
+        }
+    }
+
+    /**
+     * Utility method to get the contents of the given stream, up to the next
+     * ']', (ignoring pairs of brackets '[' and ']')
+     *
+     * @param stream
+     * @return
+     * @throws IOException
+     */
+    private StringReader getStreamUpToMatchingBracket(StringReader stream)
+            throws IOException {
+        StringBuffer sb = new StringBuffer();
+        int foundBracket = -1;
+        while (foundBracket != 0) {
+            char c = (char) stream.read();
+            if (c == ']') {
+                foundBracket++;
+            }
+            if (c == '[') {
+                foundBracket--;
+            }
+            if (!(foundBracket == 0)) {
+                sb.append(c);
+            }
+        }
+        return new StringReader(sb.toString());
+    }
+
+    private int readInteger(int i, InputStream stream) {
+        int result = 0;
+        for (int j = 0; j < i; j++) {
+            try {
+                result = result << 8 | stream.read();
+            } catch (IOException e) {
+                throw new RuntimeException("Error reading unknown attribute");
+            }
+        }
+        // use casting to preserve sign
+        if(i == 1) result = (byte) result;
+        if(i == 2) result = (short) result;
+        return result;
+    }
+
+    /**
+     * Returns the {@link BHSDCodec} that should be used for the given layout
+     * element
+     *
+     * @param layoutElement
+     */
+    private BHSDCodec getCodec(String layoutElement) {
+        if (layoutElement.indexOf('O') >= 0) {
+            return Codec.BRANCH5;
+        } else if (layoutElement.indexOf('P') >= 0) {
+            return Codec.BCI5;
+        } else if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$
+                && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$
+            return Codec.SIGNED5;
+        } else if (layoutElement.indexOf('B') >= 0) {
+            return Codec.BYTE1;
+        } else {
+            return Codec.UNSIGNED5;
+        }
+    }
+
+    /**
+     * Utility method to get the contents of the given stream, up to the next
+     * ']', (ignoring pairs of brackets '[' and ']')
+     *
+     * @param stream
+     * @return
+     * @throws IOException
+     */
+    private String readUpToMatchingBracket(StringReader stream)
+            throws IOException {
+        StringBuffer sb = new StringBuffer();
+        int foundBracket = -1;
+        while (foundBracket != 0) {
+            char c = (char) stream.read();
+            if (c == ']') {
+                foundBracket++;
+            }
+            if (c == '[') {
+                foundBracket--;
+            }
+            if (!(foundBracket == 0)) {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Read a number from the stream and return it
+     *
+     * @param stream
+     * @return
+     * @throws IOException
+     */
+    private Integer readNumber(StringReader stream) throws IOException {
+        stream.mark(1);
+        char first = (char) stream.read();
+        boolean negative = first == '-';
+        if (!negative) {
+            stream.reset();
+        }
+        stream.mark(100);
+        int i;
+        int length = 0;
+        while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) {
+            length++;
+        }
+        stream.reset();
+        if(length == 0) {
+            return null;
+        }
+        char[] digits = new char[length];
+        int read = stream.read(digits);
+        if (read != digits.length) {
+            throw new IOException("Error reading from the input stream");
+        }
+        return new Integer(Integer.parseInt((negative ? "-" : "") + new String(digits)));
+    }
+
+    /**
+     * Read a 'body' section of the layout from the given stream
+     *
+     * @param stream
+     * @return List of LayoutElements
+     * @throws IOException
+     */
+    private List readBody(StringReader stream) throws IOException {
+        List layoutElements = new ArrayList();
+        LayoutElement e;
+        while ((e = readNextLayoutElement(stream)) != null) {
+            layoutElements.add(e);
+        }
+        return layoutElements;
+    }
+
+    /**
+     * Renumber any bytecode indexes or offsets as described in section 5.5.2 of
+     * the pack200 specification
+     *
+     * @param bciRenumbering
+     * @param labelsToOffsets
+     */
+    public void renumberBci(IntList bciRenumbering, Map labelsToOffsets) {
+        for (Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) {
+            AttributeLayoutElement element = (AttributeLayoutElement) iterator.next();
+            element.renumberBci(bciRenumbering, labelsToOffsets);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200Adapter.java b/src/main/java/org/apache/harmony/pack200/Pack200Adapter.java
new file mode 100644
index 0000000..e2b284c
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200Adapter.java
@@ -0,0 +1,59 @@
+/*
+ *  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.harmony.pack200;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Provides generic JavaBeans support for the Pack/UnpackAdapters
+ */
+public abstract class Pack200Adapter {
+
+	protected static final int DEFAULT_BUFFER_SIZE = 8192;
+
+	private final PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+	private final SortedMap<String, String> properties = new TreeMap<String, String>();
+
+	public SortedMap<String, String> properties() {
+		return properties;
+	}
+
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		support.addPropertyChangeListener(listener);
+	}
+
+	protected void firePropertyChange(String propertyName, Object oldValue,
+			Object newValue) {
+		support.firePropertyChange(propertyName, oldValue, newValue);
+	}
+
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		support.removePropertyChangeListener(listener);
+	}
+
+	/**
+	 * Completion between 0..1
+	 * @param value
+	 */
+	protected void completed(double value) {
+		firePropertyChange("pack.progress", null, String.valueOf((int)(100*value))); //$NON-NLS-1$
+	}
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java b/src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java
new file mode 100644
index 0000000..29c024f
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200ClassReader.java
@@ -0,0 +1,81 @@
+/*
+ *  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.harmony.pack200;
+
+import org.objectweb.asm.ClassReader;
+
+/**
+ * Wrapper for ClassReader that enables pack200 to obtain extra class file
+ * information
+ */
+public class Pack200ClassReader extends ClassReader {
+
+    private boolean lastConstantHadWideIndex;
+    private int lastUnsignedShort;
+    private boolean anySyntheticAttributes;
+    private String fileName;
+
+    /**
+     * @param b
+     *            the contents of class file in the format of bytes
+     */
+    public Pack200ClassReader(byte[] b) {
+        super(b);
+    }
+
+    public int readUnsignedShort(int index) {
+        // Doing this to check whether last load-constant instruction was ldc (18) or ldc_w (19)
+        // TODO:  Assess whether this impacts on performance
+        int unsignedShort = super.readUnsignedShort(index);
+        if(b[index - 1] == 19) {
+            lastUnsignedShort = unsignedShort;
+        } else {
+            lastUnsignedShort = Short.MIN_VALUE;
+        }
+        return unsignedShort;
+    }
+
+    public Object readConst(int item, char[] buf) {
+        lastConstantHadWideIndex = item == lastUnsignedShort;
+        return super.readConst(item, buf);
+    }
+
+    public String readUTF8(int arg0, char[] arg1) {
+        String utf8 = super.readUTF8(arg0, arg1);
+        if(!anySyntheticAttributes && "Synthetic".equals(utf8)) {
+            anySyntheticAttributes = true;
+        }
+        return utf8;
+    }
+
+    public boolean lastConstantHadWideIndex() {
+        return lastConstantHadWideIndex;
+    }
+
+    public boolean hasSyntheticAttributes() {
+        return anySyntheticAttributes;
+    }
+
+    public void setFileName(String name) {
+        this.fileName = name;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200Exception.java b/src/main/java/org/apache/harmony/pack200/Pack200Exception.java
new file mode 100644
index 0000000..cb4d5e2
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200Exception.java
@@ -0,0 +1,36 @@
+/*
+ *  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.harmony.pack200;
+
+/**
+ * Represents a problem with a Pack200 coding/decoding issue.
+ */
+public class Pack200Exception extends Exception {
+
+    private static final long serialVersionUID = 5168177401552611803L;
+
+    /**
+     * Create a new Pack200 exception with the given message
+     *
+     * @param message
+     *            the text message to display
+     */
+    public Pack200Exception(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/Pack200PackerAdapter.java b/src/main/java/org/apache/harmony/pack200/Pack200PackerAdapter.java
new file mode 100644
index 0000000..bc63a3d
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/Pack200PackerAdapter.java
@@ -0,0 +1,102 @@
+/*
+ *  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.harmony.pack200;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import org.apache.commons.compress.java.util.jar.Pack200.Packer;
+
+
+/**
+ * This class provides the binding between the standard Pack200 interface and the
+ * internal interface for (un)packing. As this uses generics for the SortedMap,
+ * this class must be compiled and run on a Java 1.5 system. However, Java 1.5
+ * is not necessary to use the internal libraries for unpacking.
+ */
+public class Pack200PackerAdapter extends Pack200Adapter implements Packer {
+
+    private final PackingOptions options = new PackingOptions();
+
+    public void pack(JarFile file, OutputStream out) throws IOException {
+        if (file == null || out == null)
+            throw new IllegalArgumentException(
+                    "Must specify both input and output streams");
+        completed(0);
+        try {
+            new org.apache.harmony.pack200.Archive(file, out, options).pack();
+        } catch (Pack200Exception e) {
+            throw new IOException("Failed to pack Jar:" + String.valueOf(e));
+        }
+        completed(1);
+    }
+
+    public void pack(JarInputStream in, OutputStream out) throws IOException {
+        if (in == null || out == null)
+            throw new IllegalArgumentException(
+                    "Must specify both input and output streams");
+        completed(0);
+        PackingOptions options = new PackingOptions();
+
+        try {
+            new org.apache.harmony.pack200.Archive(in, out, options).pack();
+        } catch (Pack200Exception e) {
+            throw new IOException("Failed to pack Jar:" + String.valueOf(e));
+        }
+        completed(1);
+        in.close();
+    }
+
+    protected void firePropertyChange(String propertyName, Object oldValue,
+            Object newValue) {
+        super.firePropertyChange(propertyName, oldValue, newValue);
+        if(newValue != null && !newValue.equals(oldValue)) {
+            if (propertyName.startsWith(CLASS_ATTRIBUTE_PFX)) {
+                String attributeName = propertyName.substring(CLASS_ATTRIBUTE_PFX.length());
+                options.addClassAttributeAction(attributeName, (String)newValue);
+            } else if (propertyName.startsWith(CODE_ATTRIBUTE_PFX)) {
+                String attributeName = propertyName.substring(CODE_ATTRIBUTE_PFX.length());
+                options.addCodeAttributeAction(attributeName, (String)newValue);
+            } else if (propertyName.equals(DEFLATE_HINT)) {
+                options.setDeflateHint((String) newValue);
+            } else if (propertyName.equals(EFFORT)) {
+                options.setEffort(Integer.parseInt((String)newValue));
+            } else if (propertyName.startsWith(FIELD_ATTRIBUTE_PFX)) {
+                String attributeName = propertyName.substring(FIELD_ATTRIBUTE_PFX.length());
+                options.addFieldAttributeAction(attributeName, (String)newValue);
+            } else if (propertyName.equals(KEEP_FILE_ORDER)) {
+                options.setKeepFileOrder(Boolean.parseBoolean((String)newValue));
+            } else if (propertyName.startsWith(METHOD_ATTRIBUTE_PFX)) {
+                String attributeName = propertyName.substring(METHOD_ATTRIBUTE_PFX.length());
+                options.addMethodAttributeAction(attributeName, (String)newValue);
+            } else if (propertyName.equals(MODIFICATION_TIME)) {
+                options.setModificationTime((String)newValue);
+            } else if (propertyName.startsWith(PASS_FILE_PFX)) {
+                if(oldValue != null && !oldValue.equals("")) {
+                    options.removePassFile((String)oldValue);
+                }
+                options.addPassFile((String) newValue);
+            } else if (propertyName.equals(SEGMENT_LIMIT)) {
+                options.setSegmentLimit(Long.parseLong((String)newValue));
+            } else if (propertyName.equals(UNKNOWN_ATTRIBUTE)) {
+                options.setUnknownAttributeAction((String)newValue);
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/harmony/pack200/PackingOptions.java b/src/main/java/org/apache/harmony/pack200/PackingOptions.java
new file mode 100644
index 0000000..943ca2f
--- /dev/null
+++ b/src/main/java/org/apache/harmony/pack200/PackingOptions.java
@@ -0,0 +1,363 @@
+/*
+ *  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.harmony.pack200;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Attribute;
+
+/**
+ * Utility class to manage the various options available for pack200
+ */
+public class PackingOptions {
+
+    public static final String STRIP = "strip";
+    public static final String ERROR = "error";
+    public static final String PASS = "pass";
+    public static final String KEEP = "keep";
+
+    // All options are initially set to their defaults
+    private boolean gzip = true;
+    private boolean stripDebug = false;
+    private boolean keepFileOrder = true;
+    private long segmentLimit = 1000000L;
+    private int effort = 5;
+    private String deflateHint = KEEP;
+    private String modificationTime = KEEP;
+    private List passFiles;
+    private String unknownAttributeAction = PASS;
+    private Map classAttributeActions;
+    private Map fieldAttributeActions;
+    private Map methodAttributeActions;
+    private Map codeAttributeActions;
+    private boolean verbose = false;
+    private String logFile;
+
+    private Attribute[] unknownAttributeTypes;
+
+    public boolean isGzip() {
+        return gzip;
+    }
+
+    public void setGzip(boolean gzip) {
+        this.gzip = gzip;
+    }
+
+    public boolean isStripDebug() {
+        return stripDebug;
+    }
+
+    /**
+     * Set strip debug attributes. If true, all debug attributes (i.e.
+     * LineNumberTable, SourceFile, LocalVariableTable and
+     * LocalVariableTypeTable attributes) are stripped when reading the input
+     * class files and not included in the output archive.
+     *
+     * @param stripDebug
+     */
+    public void setStripDebug(boolean stripDebug) {
+        this.stripDebug = stripDebug;
+    }
+
+    public boolean isKeepFileOrder() {
+        return keepFileOrder;
+    }
+
+    public void setKeepFileOrder(boolean keepFileOrder) {
+        this.keepFileOrder = keepFileOrder;
+    }
+
+    public long getSegmentLimit() {
+        return segmentLimit;
+    }
+
+    /**
+     * Set the segment limit (equivalent to -S command line option)
+     * @param segmentLimit - the limit in bytes
+     */
+    public void setSegmentLimit(long segmentLimit) {
+        this.segmentLimit = segmentLimit;
+    }
+
+    public int getEffort() {
+        return effort;
+    }
+
+    /**
+     * Set the compression effort level (0-9, equivalent to -E command line option)
+     * @param effort
+     */
+    public void setEffort(int effort) {
+        this.effort = effort;
+    }
+
+    public String getDeflateHint() {
+        return deflateHint;
+    }
+
+    public boolean isKeepDeflateHint() {
+        return KEEP.equals(deflateHint);
+    }
+
+    public void setDeflateHint(String deflateHint) {
+        if (!KEEP.equals(deflateHint)
+                && !"true".equals(deflateHint)
+                && !"false".equals(deflateHint)) {
+            throw new IllegalArgumentException(
+                    "Bad argument: -H "
+                            + deflateHint
+                            + " ? deflate hint should be either true, false or keep (default)");
+        }
+        this.deflateHint = deflateHint;
+    }
+
+    public String getModificationTime() {
+        return modificationTime;
+    }
+
+    public void setModificationTime(String modificationTime) {
+        if (!KEEP.equals(modificationTime)
+                && !"latest".equals(modificationTime)) {
+            throw new IllegalArgumentException(
+                    "Bad argument: -m "
+                            + modificationTime
+                            + " ? transmit modtimes should be either latest or keep (default)");
+        }
+        this.modificationTime = modificationTime;
+    }
+
+    public boolean isPassFile(String passFileName) {
+        if (passFiles != null) {
+            for (Iterator iterator = passFiles.iterator(); iterator.hasNext();) {
+                String pass = (String) iterator.next();
+                if (passFileName.equals(pass)) {
+                    return true;
+                } else if (!pass.endsWith(".class")) { // a whole directory is
+                    // passed
+                    if (!pass.endsWith("/")) {
+                        // Make sure we don't get any false positives (e.g.
+                        // exclude "org/apache/harmony/pack" should not match
+                        // files under "org/apache/harmony/pack200/")
+                        pass = pass + "/";
+                    }
+                    return passFileName.startsWith(pass);
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Tell the compressor to pass the file with the given name, or if the name
+     * is a directory name all files under that directory will be passed.
+     *
+     * @param passFileName
+     *            the file name
+     */
+    public void addPassFile(String passFileName) {
+        if(passFiles == null) {
+            passFiles = new ArrayList();
+        }
+        String fileSeparator = System.getProperty("file.separator");
+        if(fileSeparator.equals("\\")) {
+            // Need to escape backslashes for replaceAll(), which uses regex
+            fileSeparator += "\\";
+        }
+        passFileName = passFileName.replaceAll(fileSeparator, "/");
+        passFiles.add(passFileName);
+    }
+
+    public void removePassFile(String passFileName) {
+        passFiles.remove(passFileName);
+    }
+
+    public String getUnknownAttributeAction() {
+        return unknownAttributeAction;
+    }
+
+    /**
+     * Tell the compressor what to do if an unknown attribute is encountered
+     * @param unknownAttributeAction - the action to perform
+     */
+    public void setUnknownAttributeAction(String unknownAttributeAction) {
+        this.unknownAttributeAction = unknownAttributeAction;
+        if (!PASS.equals(unknownAttributeAction)
+                && !ERROR.equals(unknownAttributeAction)
+                && !STRIP.equals(unknownAttributeAction)) {
+            throw new RuntimeException("Incorrect option for -U, "
+                    + unknownAttributeAction);
+        }
+    }
+
+    public void addClassAttributeAction(String attributeName, String action) {
+        if(classAttributeActions == null) {
+            classAttributeActions = new HashMap();
+        }
+        classAttributeActions.put(attributeName, action);
+    }
+
+    public void addFieldAttributeAction(String attributeName, String action) {
+        if(fieldAttributeActions == null) {
+            fieldAttributeActions = new HashMap();
+        }
+        fieldAttributeActions.put(attributeName, action);
... 17684 lines suppressed ...