You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by hi...@apache.org on 2009/06/17 16:00:28 UTC

svn commit: r785606 [2/4] - in /harmony/enhanced/classlib/branches/java6: ./ make/ modules/archive/src/main/java/java/util/jar/ modules/archive/src/main/java/java/util/zip/ modules/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ m...

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/AttributeDefinitionBands.java Wed Jun 17 14:00:24 2009
@@ -24,9 +24,11 @@
 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.
+ * Attribute Definition bands define how any unknown attributes should be read
+ * by the decompressor.
  */
 public class AttributeDefinitionBands extends BandSet {
 
@@ -35,98 +37,132 @@
     public static final int CONTEXT_FIELD = 1;
     public static final int CONTEXT_METHOD = 2;
 
-    private final Map layouts = new HashMap();
-
-    private final Map classAttributes = new HashMap();
-    private final Map methodAttributes = new HashMap();
-    private final Map fieldAttributes = new HashMap();
-    private final Map codeAttributes = new HashMap();
+    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) {
+    public AttributeDefinitionBands(Segment segment, int effort,
+            Attribute[] attributePrototypes) {
         super(effort, segment.getSegmentHeader());
         this.cpBands = segment.getCpBands();
         this.segment = segment;
-    }
-
-    public void finaliseBands() {
-        addSyntheticDefinitions();
-        segmentHeader.setAttribute_definition_count(classAttributes.keySet()
-                .size()
-                + methodAttributes.keySet().size()
-                + fieldAttributes.keySet().size()
-                + codeAttributes.keySet().size()
-                + attributeDefinitions.size());
-        if (classAttributes.keySet().size() > 7) {
+        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.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(methodAttributes.keySet().size() > 6) {
+        if (methodLayouts.keySet().size() > 6) {
             segmentHeader.setHave_method_flags_hi(true);
         }
-        if(fieldAttributes.keySet().size() > 10) {
+        if (fieldLayouts.keySet().size() > 10) {
             segmentHeader.setHave_field_flags_hi(true);
         }
-        if(codeAttributes.keySet().size() > 15) {
+        if (codeLayouts.keySet().size() > 15) {
             segmentHeader.setHave_code_flags_hi(true);
         }
-    }
-
-    public void pack(OutputStream out) throws IOException, Pack200Exception {
-        int[] availableClassIndices = new int[] {25, 26, 27, 28, 29, 30, 31};
-        if(classAttributes.size() > 7) {
+        int[] availableClassIndices = new int[] { 25, 26, 27, 28, 29, 30, 31 };
+        if (classLayouts.size() > 7) {
             availableClassIndices = addHighIndices(availableClassIndices);
         }
-        addAttributeDefinitions(classAttributes, availableClassIndices, CONTEXT_CLASS);
-        int[] availableMethodIndices = new int[] {26, 27, 28, 29, 30, 31};
-        if(methodAttributes.size() > 6) {
+        addAttributeDefinitions(classLayouts, availableClassIndices,
+                CONTEXT_CLASS);
+        int[] availableMethodIndices = new int[] { 26, 27, 28, 29, 30, 31 };
+        if (methodAttributeLayouts.size() > 6) {
             availableMethodIndices = addHighIndices(availableMethodIndices);
         }
-        addAttributeDefinitions(methodAttributes, availableMethodIndices, CONTEXT_METHOD);
-        int[] availableFieldIndices = new int[] {18, 23, 24, 25, 26, 27, 28, 29, 30, 31};
-        if(fieldAttributes.size() > 10) {
+        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(fieldAttributes, availableFieldIndices, CONTEXT_FIELD);
-        int[] availableCodeIndices = new int[] {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
-        if(codeAttributes.size() > 15) {
+        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(codeAttributes, availableCodeIndices, CONTEXT_CODE);
+        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 {
         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);
+            AttributeDefinition def = (AttributeDefinition) attributeDefinitions
+                    .get(i);
+            attributeDefinitionHeader[i] = def.contextType
+                    | (def.index + 1 << 2);
             attributeDefinitionName[i] = def.name.getIndex();
             attributeDefinitionLayout[i] = def.layout.getIndex();
         }
 
-        out.write(encodeBandInt("attributeDefinitionHeader", attributeDefinitionHeader, Codec.BYTE1));
-        out.write(encodeBandInt("attributeDefinitionName", attributeDefinitionName, Codec.UNSIGNED5));
-        out.write(encodeBandInt("attributeDefinitionLayout", attributeDefinitionLayout, Codec.UNSIGNED5));
+        out.write(encodeBandInt("attributeDefinitionHeader",
+                attributeDefinitionHeader, Codec.BYTE1));
+        out.write(encodeBandInt("attributeDefinitionName",
+                attributeDefinitionName, Codec.UNSIGNED5));
+        out.write(encodeBandInt("attributeDefinitionLayout",
+                attributeDefinitionLayout, Codec.UNSIGNED5));
     }
 
     private void addSyntheticDefinitions() {
-        boolean anySytheticClasses = segment.getClassBands().isAnySyntheticClasses();
-        boolean anySyntheticMethods = segment.getClassBands().isAnySyntheticMethods();
-        boolean anySyntheticFields = segment.getClassBands().isAnySyntheticFields();
-        if(anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
+        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 (anySytheticClasses) {
+                attributeDefinitions.add(new AttributeDefinition(12,
+                        CONTEXT_CLASS, syntheticUTF, emptyUTF));
             }
-            if(anySyntheticMethods) {
-                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_METHOD, syntheticUTF, emptyUTF));
+            if (anySyntheticMethods) {
+                attributeDefinitions.add(new AttributeDefinition(12,
+                        CONTEXT_METHOD, syntheticUTF, emptyUTF));
             }
-            if(anySyntheticFields) {
-                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_FIELD, syntheticUTF, emptyUTF));
+            if (anySyntheticFields) {
+                attributeDefinitions.add(new AttributeDefinition(12,
+                        CONTEXT_FIELD, syntheticUTF, emptyUTF));
             }
         }
     }
@@ -137,29 +173,58 @@
             temp[i] = availableIndices[i];
         }
         int j = 32;
-        for (int i = availableIndices.length; i < temp.length ; i++) {
+        for (int i = availableIndices.length; i < temp.length; i++) {
             temp[i] = j;
             j++;
         }
         return temp;
     }
 
-    private void addAttributeDefinitions(Map attributes,
-            int[] availableIndices, int contextType) {
+    private void addAttributeDefinitions(Map layouts, int[] availableIndices,
+            int contextType) {
         int i = 0;
-        for (Iterator iterator = attributes.keySet().iterator(); iterator.hasNext();) {
+        for (Iterator iterator = layouts.keySet().iterator(); iterator
+                .hasNext();) {
             String name = (String) iterator.next();
             String layout = (String) layouts.get(name);
             int index = availableIndices[i];
-            attributeDefinitions.add(new AttributeDefinition(index, contextType, cpBands.getCPUtf8(name), cpBands.getCPUtf8(layout)));
+            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 void addLayout(String name, String layout) {
-        layouts.put(name, layout);
+    public List getClassAttributeLayouts() {
+        return classAttributeLayouts;
+    }
+
+    public List getMethodAttributeLayouts() {
+        return methodAttributeLayouts;
+    }
+
+    public List getFieldAttributeLayouts() {
+        return fieldAttributeLayouts;
+    }
+
+    public List getCodeAttributeLayouts() {
+        return codeAttributeLayouts;
     }
 
-    private static class AttributeDefinition {
+    public static class AttributeDefinition {
 
         public int index;
         public int contextType;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BHSDCodec.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BHSDCodec.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BHSDCodec.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BHSDCodec.java Wed Jun 17 14:00:24 2009
@@ -204,14 +204,14 @@
         return cardinality;
     }
 
-    public long decode(InputStream in) throws IOException, Pack200Exception {
+    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 long decode(InputStream in, long last) throws IOException,
+    public int decode(InputStream in, long last) throws IOException,
             Pack200Exception {
         int n = 0;
         long z = 0;
@@ -219,6 +219,7 @@
 
         do {
             x = in.read();
+            lastBandLength ++;
             z += x * powers[n];
             n++;
         } while (x >= l && n < b);
@@ -252,7 +253,39 @@
         // }
         if (isDelta())
             z += last;
-        return z;
+        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) {
@@ -269,19 +302,19 @@
      * @return <code>true</code> if the encoding can encode this value
      */
     public boolean encodes(long value) {
-        return (value >= smallest && value <= largest);
+        return value >= smallest && value <= largest;
     }
 
-    public byte[] encode(long value, long last) throws Pack200Exception {
-        if (isDelta()) {
-            value -= last;
+    public byte[] encode(int value, int last) throws Pack200Exception {
+        if(!encodes(value)) {
+            throw new Pack200Exception("The codec " + toString()
+                  + " does not encode the value " + value);
         }
-        // TODO: Do we need this?  this implementation isn't right because of integer overflow...
-//        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;
@@ -301,12 +334,16 @@
             if (z < 0) {
                 // Need to use integer overflow here to represent negatives.
                 if(cardinality < 4294967296L) {
-                    z+= cardinality;
+                    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;
@@ -331,7 +368,7 @@
         return bytes;
     }
 
-    public byte[] encode(long value) throws Pack200Exception {
+    public byte[] encode(int value) throws Pack200Exception {
         return encode(value, 0);
     }
 
@@ -393,19 +430,16 @@
 
     private long calculateSmallest() {
         long result;
-        if (d == 1) {
-            BHSDCodec bh0 = new BHSDCodec(b, h);
-            return bh0.smallest();
-        } else if (isSigned()) {
-            result = -cardinality() / (1 << s);
-        } else {
-            if (cardinality > Integer.MAX_VALUE) {
+        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 Math.max(Integer.MIN_VALUE, result);
+        return result;
     }
 
     /**

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BandSet.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BandSet.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BandSet.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BandSet.java Wed Jun 17 14:00:24 2009
@@ -18,49 +18,117 @@
 
 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 {
 
-    private final int effort;
     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 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 {
-        if(effort > 1 && (ints.length > 99 || effort == 9)) {
-            Codec betterCodec = lookForBetterCodec(name, ints, defaultCodec);
+        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 > 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[] rest = betterCodec.encode(ints);
-                    byte[] band = new byte[specifierEncoded.length + rest.length];
+                    byte[] band = new byte[specifierEncoded.length + encodedBand.length];
                     System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
-                    System.arraycopy(rest, 0, band, specifierEncoded.length, rest.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) {
 
                 }
@@ -69,39 +137,209 @@
 
         // If we get here then we've decided to use the default codec.
         if(ints.length > 0) {
-//            System.out.println("encoding " + name + ", size = " + ints.length);
+            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[] rest = defaultCodec.encode(ints);
-                    byte[] band = new byte[specifierEncoded.length + rest.length];
+                    byte[] band = new byte[specifierEncoded.length + encodedBand.length];
                     System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
-                    System.arraycopy(rest, 0, band, specifierEncoded.length, rest.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[] rest = defaultCodec.encode(ints);
-                    byte[] band = new byte[specifierEncoded.length + rest.length];
+                    byte[] band = new byte[specifierEncoded.length + encodedBand.length];
                     System.arraycopy(specifierEncoded, 0, band, 0, specifierEncoded.length);
-                    System.arraycopy(rest, 0, band, specifierEncoded.length, rest.length);
+                    System.arraycopy(encodedBand, 0, band, specifierEncoded.length, encodedBand.length);
                     return band;
                 }
             }
-            return defaultCodec.encode(ints);
+            return encodedBand;
         }
         return new byte[0];
     }
 
-    private Codec lookForBetterCodec(String name, int[] ints,
-            BHSDCodec defaultCodec) {
-        // TODO Auto-generated method stub
-        return null;
+    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;
+            }
+        }
+    }
+
+    /**
+     * Returns true if the name of the source file can be predicted from the
+     * class name
+     *
+     * @param className
+     *            the class name
+     * @param sourceFileName
+     *            the source file name
+     */
     public boolean isPredictableSourceFileName(String className, String sourceFileName) {
         if (className.indexOf('.') != -1) {
             className = className.substring(className.lastIndexOf('.') + 1);
@@ -113,6 +351,218 @@
         return className.equals(sourceFileName);
     }
 
+// 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 = listToArray(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) {
@@ -139,6 +589,9 @@
         }
     }
 
+    /**
+     * Converts a list of Integers to an int[] array
+     */
     protected int[] listToArray(List integerList) {
         int[] array = new int[integerList.size()];
         for (int i = 0; i < array.length; i++) {
@@ -147,6 +600,9 @@
         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++) {
@@ -155,6 +611,9 @@
         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++) {
@@ -166,6 +625,10 @@
         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++) {
@@ -199,4 +662,144 @@
         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;
+
+    }
+
 }

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BcBands.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BcBands.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BcBands.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/BcBands.java Wed Jun 17 14:00:24 2009
@@ -26,7 +26,8 @@
 import org.objectweb.asm.Label;
 
 /**
- * Bytecode bands
+ * Bytecode bands (corresponds to the <code>bc_bands</code> set of bands in the
+ * pack200 specification)
  */
 public class BcBands extends BandSet {
 
@@ -85,6 +86,11 @@
         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);

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPClass.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPClass.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPClass.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPClass.java Wed Jun 17 14:00:24 2009
@@ -16,7 +16,9 @@
  */
 package org.apache.harmony.pack200;
 
-
+/**
+ * Constant pool entry for a class
+ */
 public class CPClass extends CPConstant implements Comparable {
 
     private final String className;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPConstant.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPConstant.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPConstant.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPConstant.java Wed Jun 17 14:00:24 2009
@@ -16,7 +16,11 @@
  */
 package org.apache.harmony.pack200;
 
-
-public abstract class CPConstant extends ConstantPoolEntry implements Comparable {
+/**
+ * Abstract superclass for constant pool constant entries such as numbers or
+ * Strings
+ */
+public abstract class CPConstant extends ConstantPoolEntry implements
+        Comparable {
 
 }

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPDouble.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPDouble.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPDouble.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPDouble.java Wed Jun 17 14:00:24 2009
@@ -16,10 +16,12 @@
  */
 package org.apache.harmony.pack200;
 
-
+/**
+ * Constant pool entry for a double.
+ */
 public class CPDouble extends CPConstant {
 
-    private double theDouble;
+    private final double theDouble;
 
     public CPDouble(double theDouble) {
         this.theDouble = theDouble;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPFloat.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPFloat.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPFloat.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPFloat.java Wed Jun 17 14:00:24 2009
@@ -16,10 +16,12 @@
  */
 package org.apache.harmony.pack200;
 
-
+/**
+ * Constant pool entry for a float.
+ */
 public class CPFloat extends CPConstant {
 
-    private float theFloat;
+    private final float theFloat;
 
     public CPFloat(float theFloat) {
         this.theFloat = theFloat;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPInt.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPInt.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPInt.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPInt.java Wed Jun 17 14:00:24 2009
@@ -16,7 +16,9 @@
  */
 package org.apache.harmony.pack200;
 
-
+/**
+ * Constant pool entry for an int.
+ */
 public class CPInt extends CPConstant {
 
     private final int theInt;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPLong.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPLong.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPLong.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPLong.java Wed Jun 17 14:00:24 2009
@@ -16,7 +16,9 @@
  */
 package org.apache.harmony.pack200;
 
-
+/**
+ * Constant pool entry for a long.
+ */
 public class CPLong extends CPConstant {
 
     private final long theLong;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPMethodOrField.java Wed Jun 17 14:00:24 2009
@@ -16,6 +16,9 @@
  */
 package org.apache.harmony.pack200;
 
+/**
+ * Constant pool entry for a method or field.
+ */
 public class CPMethodOrField extends ConstantPoolEntry implements Comparable {
 
     private final CPClass className;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPNameAndType.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPNameAndType.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPNameAndType.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPNameAndType.java Wed Jun 17 14:00:24 2009
@@ -16,6 +16,9 @@
  */
 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;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPSignature.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPSignature.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPSignature.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPSignature.java Wed Jun 17 14:00:24 2009
@@ -18,6 +18,9 @@
 
 import java.util.List;
 
+/**
+ * Constant pool entry for a signature.
+ */
 public class CPSignature extends ConstantPoolEntry implements Comparable {
 
     private final CPUTF8 signatureForm;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPString.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPString.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPString.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPString.java Wed Jun 17 14:00:24 2009
@@ -16,7 +16,9 @@
  */
 package org.apache.harmony.pack200;
 
-
+/**
+ * Constant pool entry for a String.
+ */
 public class CPString extends CPConstant {
 
     private final String string;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPUTF8.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPUTF8.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPUTF8.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/CPUTF8.java Wed Jun 17 14:00:24 2009
@@ -16,7 +16,9 @@
  */
 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;

Modified: harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/ClassBands.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/ClassBands.java?rev=785606&r1=785605&r2=785606&view=diff
==============================================================================
--- harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/ClassBands.java (original)
+++ harmony/enhanced/classlib/branches/java6/modules/pack200/src/main/java/org/apache/harmony/pack200/ClassBands.java Wed Jun 17 14:00:24 2009
@@ -19,6 +19,8 @@
 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;
@@ -26,11 +28,15 @@
 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.Attribute;
 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;
@@ -77,6 +83,7 @@
     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();
@@ -103,6 +110,11 @@
     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();
@@ -114,9 +126,20 @@
     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) {
+    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();
@@ -143,16 +166,32 @@
         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);
-    }
 
-    private int index = 0;
+        createNewAttributeBands();
+    }
 
-    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;
+    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) {
@@ -232,6 +271,11 @@
         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++) {
@@ -332,6 +376,8 @@
         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());
         }
@@ -359,9 +405,61 @@
         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 {
@@ -440,6 +538,10 @@
                 cpEntryListToArray(fieldSignature), Codec.UNSIGNED5));
         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)
@@ -460,14 +562,25 @@
         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 {
         out.write(encodeFlags("class_flags", class_flags, Codec.UNSIGNED5,
                 Codec.UNSIGNED5, segmentHeader.have_class_flags_hi()));
+
+        // 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)]
+
         out.write(encodeBandInt("class_attr_calls", class_attr_calls, Codec.UNSIGNED5));
         out.write(encodeBandInt("classSourceFile",
                 cpEntryOrNullListToArray(classSourceFile), Codec.UNSIGNED5));
@@ -490,6 +603,10 @@
                 classFileVersionMinor.toArray(), Codec.UNSIGNED5));
         out.write(encodeBandInt("classFileVersionMajor",
                 classFileVersionMajor.toArray(), Codec.UNSIGNED5));
+        for (Iterator iterator = classAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            bands.pack(out);
+        }
     }
 
     private int[] getInts(CPClass[] cpClasses) {
@@ -527,7 +644,8 @@
 
         // *code_attr_count :UNSIGNED5 [COUNT(1<<16,...)]
         // *code_attr_indexes :UNSIGNED5 [SUM(*code_attr_count)]
-        // *code_attr_calls :UNSIGNED5 [...]
+        out.write(encodeBandInt("code_attr_calls", code_attr_calls,
+                Codec.UNSIGNED5));
         out.write(encodeBandInt("code_LineNumberTable_N",
                 codeLineNumberTableN.toArray(), Codec.UNSIGNED5));
         out.write(encodeBandInt("code_LineNumberTable_bci_P",
@@ -562,7 +680,10 @@
                 Codec.UNSIGNED5));
         out.write(encodeBandInt("code_LocalVariableTypeTable_slot",
                 codeLocalVariableTypeTableSlot.toArray(), Codec.UNSIGNED5));
-
+        for (Iterator iterator = codeAttributeBands.iterator(); iterator.hasNext();) {
+            NewAttributeBands bands = (NewAttributeBands) iterator.next();
+            bands.pack(out);
+        }
     }
 
     public void addMethod(int flags, String name, String desc,
@@ -690,14 +811,64 @@
                 .getCPNameAndType(name, desc));
     }
 
-    public void addUnknownFieldAttribute(Attribute arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    public void addUnknownMethodAttribute(Attribute arg0) {
-        // TODO Auto-generated method stub
-
+    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) {
@@ -713,10 +884,10 @@
         codeMaxLocals.add(maxLocals);
     }
 
-    public void addCode(boolean stripDebug) {
+    public void addCode() {
         codeHandlerCount.add(0);
         if(!stripDebug) {
-            codeFlags.add(new Long((1 << 2))); // TODO: What if there's no debug information?
+            codeFlags.add(new Long((1 << 2)));
             codeLocalVariableTableN.add(0);
         }
     }
@@ -743,7 +914,6 @@
         }
         codeLineNumberTableLine.add(line);
         codeLineNumberTableBciP.add(start);
-        // TODO: bci renumbering
     }
 
     public void addLocalVariable(String name, String desc, String signature,
@@ -790,6 +960,23 @@
                 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) {
@@ -946,4 +1133,136 @@
         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();
+        index--;
+    }
 }