    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.pack200;
    018
    019import java.io.IOException;
    020import java.io.OutputStream;
    021import java.util.ArrayList;
    022import java.util.HashMap;
    023import java.util.Iterator;
    024import java.util.List;
    025import java.util.Map;
    026
    027import org.objectweb.asm.Attribute;
    028
    029/**
    030 * Attribute Definition bands define how any unknown attributes should be read by the decompressor.
    031 */
    032public class AttributeDefinitionBands extends BandSet {
    033
    034    public static final int CONTEXT_CLASS = 0;
    035    public static final int CONTEXT_CODE = 3;
    036    public static final int CONTEXT_FIELD = 1;
    037    public static final int CONTEXT_METHOD = 2;
    038
    039    private final List classAttributeLayouts = new ArrayList();
    040    private final List methodAttributeLayouts = new ArrayList();
    041    private final List fieldAttributeLayouts = new ArrayList();
    042    private final List codeAttributeLayouts = new ArrayList();
    043
    044    private final List attributeDefinitions = new ArrayList();
    045
    046    private final CpBands cpBands;
    047    private final Segment segment;
    048
    049    public AttributeDefinitionBands(final Segment segment, final int effort, final Attribute[] attributePrototypes) {
    050        super(effort, segment.getSegmentHeader());
    051        this.cpBands = segment.getCpBands();
    052        this.segment = segment;
    053        final Map classLayouts = new HashMap();
    054        final Map methodLayouts = new HashMap();
    055        final Map fieldLayouts = new HashMap();
    056        final Map codeLayouts = new HashMap();
    057
    058        for (int i = 0; i < attributePrototypes.length; i++) {
    059            final NewAttribute newAttribute = (NewAttribute) attributePrototypes[i];
    060            if (!(newAttribute instanceof NewAttribute.ErrorAttribute)
    061                && !(newAttribute instanceof NewAttribute.PassAttribute)
    062                && !(newAttribute instanceof NewAttribute.StripAttribute)) {
    063                if (newAttribute.isContextClass()) {
    064                    classLayouts.put(newAttribute.type, newAttribute.getLayout());
    065                }
    066                if (newAttribute.isContextMethod()) {
    067                    methodLayouts.put(newAttribute.type, newAttribute.getLayout());
    068                }
    069                if (newAttribute.isContextField()) {
    070                    fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
    071                }
    072                if (newAttribute.isContextCode()) {
    073                    codeLayouts.put(newAttribute.type, newAttribute.getLayout());
    074                }
    075            }
    076        }
    077        if (classLayouts.size() > 7) {
    078            segmentHeader.setHave_class_flags_hi(true);
    079        }
    080        if (methodLayouts.size() > 6) {
    081            segmentHeader.setHave_method_flags_hi(true);
    082        }
    083        if (fieldLayouts.size() > 10) {
    084            segmentHeader.setHave_field_flags_hi(true);
    085        }
    086        if (codeLayouts.size() > 15) {
    087            segmentHeader.setHave_code_flags_hi(true);
    088        }
    089        int[] availableClassIndices = {25, 26, 27, 28, 29, 30, 31};
    090        if (classLayouts.size() > 7) {
    091            availableClassIndices = addHighIndices(availableClassIndices);
    092        }
    093        addAttributeDefinitions(classLayouts, availableClassIndices, CONTEXT_CLASS);
    094        int[] availableMethodIndices = {26, 27, 28, 29, 30, 31};
    095        if (methodAttributeLayouts.size() > 6) {
    096            availableMethodIndices = addHighIndices(availableMethodIndices);
    097        }
    098        addAttributeDefinitions(methodLayouts, availableMethodIndices, CONTEXT_METHOD);
    099        int[] availableFieldIndices = {18, 23, 24, 25, 26, 27, 28, 29, 30, 31};
    100        if (fieldAttributeLayouts.size() > 10) {
    101            availableFieldIndices = addHighIndices(availableFieldIndices);
    102        }
    103        addAttributeDefinitions(fieldLayouts, availableFieldIndices, CONTEXT_FIELD);
    104        int[] availableCodeIndices = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
    105        if (codeAttributeLayouts.size() > 15) {
    106            availableCodeIndices = addHighIndices(availableCodeIndices);
    107        }
    108        addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
    109    }
    110
    111    /**
    112     * All input classes for the segment have now been read in, so this method is called so that this class can
    113     * calculate/complete anything it could not do while classes were being read.
    114     */
    115    public void finaliseBands() {
    116        addSyntheticDefinitions();
    117        segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
    118    }
    119
    120    @Override
    121    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    122        PackingUtils.log("Writing attribute definition bands...");
    123        final int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
    124        final int[] attributeDefinitionName = new int[attributeDefinitions.size()];
    125        final int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
    126        for (int i = 0; i < attributeDefinitionLayout.length; i++) {
    127            final AttributeDefinition def = (AttributeDefinition) attributeDefinitions.get(i);
    128            attributeDefinitionHeader[i] = def.contextType | (def.index + 1 << 2);
    129            attributeDefinitionName[i] = def.name.getIndex();
    130            attributeDefinitionLayout[i] = def.layout.getIndex();
    131        }
    132
    133        byte[] encodedBand = encodeBandInt("attributeDefinitionHeader", attributeDefinitionHeader, Codec.BYTE1);
    134        out.write(encodedBand);
    135        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionHeader["
    136            + attributeDefinitionHeader.length + "]");
    137
    138        encodedBand = encodeBandInt("attributeDefinitionName", attributeDefinitionName, Codec.UNSIGNED5);
    139        out.write(encodedBand);
    140        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionName["
    141            + attributeDefinitionName.length + "]");
    142
    143        encodedBand = encodeBandInt("attributeDefinitionLayout", attributeDefinitionLayout, Codec.UNSIGNED5);
    144        out.write(encodedBand);
    145        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionLayout["
    146            + attributeDefinitionLayout.length + "]");
    147    }
    148
    149    private void addSyntheticDefinitions() {
    150        final boolean anySytheticClasses = segment.getClassBands().isAnySyntheticClasses();
    151        final boolean anySyntheticMethods = segment.getClassBands().isAnySyntheticMethods();
    152        final boolean anySyntheticFields = segment.getClassBands().isAnySyntheticFields();
    153        if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
    154            final CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
    155            final CPUTF8 emptyUTF = cpBands.getCPUtf8("");
    156            if (anySytheticClasses) {
    157                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_CLASS, syntheticUTF, emptyUTF));
    158            }
    159            if (anySyntheticMethods) {
    160                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_METHOD, syntheticUTF, emptyUTF));
    161            }
    162            if (anySyntheticFields) {
    163                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_FIELD, syntheticUTF, emptyUTF));
    164            }
    165        }
    166    }
    167
    168    private int[] addHighIndices(final int[] availableIndices) {
    169        final int[] temp = new int[availableIndices.length + 32];
    170        for (int i = 0; i < availableIndices.length; i++) {
    171            temp[i] = availableIndices[i];
    172        }
    173        int j = 32;
    174        for (int i = availableIndices.length; i < temp.length; i++) {
    175            temp[i] = j;
    176            j++;
    177        }
    178        return temp;
    179    }
    180
    181    private void addAttributeDefinitions(final Map layouts, final int[] availableIndices, final int contextType) {
    182        final int i = 0;
    183        for (final Iterator iterator = layouts.keySet().iterator(); iterator.hasNext();) {
    184            final String name = (String) iterator.next();
    185            final String layout = (String) layouts.get(name);
    186            final int index = availableIndices[i];
    187            final AttributeDefinition definition = new AttributeDefinition(index, contextType, cpBands.getCPUtf8(name),
    188                cpBands.getCPUtf8(layout));
    189            attributeDefinitions.add(definition);
    190            switch (contextType) {
    191            case CONTEXT_CLASS:
    192                classAttributeLayouts.add(definition);
    193                break;
    194            case CONTEXT_METHOD:
    195                methodAttributeLayouts.add(definition);
    196                break;
    197            case CONTEXT_FIELD:
    198                fieldAttributeLayouts.add(definition);
    199                break;
    200            case CONTEXT_CODE:
    201                codeAttributeLayouts.add(definition);
    202            }
    203        }
    204    }
    205
    206    public List getClassAttributeLayouts() {
    207        return classAttributeLayouts;
    208    }
    209
    210    public List getMethodAttributeLayouts() {
    211        return methodAttributeLayouts;
    212    }
    213
    214    public List getFieldAttributeLayouts() {
    215        return fieldAttributeLayouts;
    216    }
    217
    218    public List getCodeAttributeLayouts() {
    219        return codeAttributeLayouts;
    220    }
    221
    222    public static class AttributeDefinition {
    223
    224        public int index;
    225        public int contextType;
    226        public CPUTF8 name;
    227        public CPUTF8 layout;
    228
    229        public AttributeDefinition(final int index, final int contextType, final CPUTF8 name, final CPUTF8 layout) {
    230            this.index = index;
    231            this.contextType = contextType;
    232            this.name = name;
    233            this.layout = layout;
    234        }
    235
    236    }
    237}