    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019
    020package org.apache.commons.compress.harmony.pack200;
    021
    022import java.io.IOException;
    023import java.io.OutputStream;
    024import java.util.ArrayList;
    025import java.util.Iterator;
    026import java.util.List;
    027
    028/**
    029 * A group of metadata (annotation) bands, such as class_RVA_bands, method_AD_bands etc.
    030 */
    031public class MetadataBandGroup extends BandSet {
    032
    033    public static final int CONTEXT_CLASS = 0;
    034    public static final int CONTEXT_FIELD = 1;
    035    public static final int CONTEXT_METHOD = 2;
    036
    037    private final String type;
    038    private int numBackwardsCalls;
    039
    040    public IntList param_NB = new IntList(); // TODO: Lazy instantiation?
    041    public IntList anno_N = new IntList();
    042    public List<CPSignature> type_RS = new ArrayList<>();
    043    public IntList pair_N = new IntList();
    044    public List<CPUTF8> name_RU = new ArrayList<>();
    045    public List<String> T = new ArrayList<>();
    046    public List<CPConstant<?>> caseI_KI = new ArrayList<>();
    047    public List<CPConstant<?>> caseD_KD = new ArrayList<>();
    048    public List<CPConstant<?>> caseF_KF = new ArrayList<>();
    049    public List<CPConstant<?>> caseJ_KJ = new ArrayList<>();
    050    public List<CPSignature> casec_RS = new ArrayList<>();
    051    public List<CPSignature> caseet_RS = new ArrayList<>();
    052    public List<CPUTF8> caseec_RU = new ArrayList<>();
    053    public List<CPUTF8> cases_RU = new ArrayList<>();
    054    public IntList casearray_N = new IntList();
    055    public List<CPSignature> nesttype_RS = new ArrayList<>();
    056    public IntList nestpair_N = new IntList();
    057    public List<CPUTF8> nestname_RU = new ArrayList<>();
    058
    059    private final CpBands cpBands;
    060    private final int context;
    061
    062    /**
    063     * Constructs a new MetadataBandGroup
    064     *
    065     * @param type          must be either AD, RVA, RIA, RVPA or RIPA.
    066     * @param context       {@code CONTEXT_CLASS}, {@code CONTEXT_METHOD} or {@code CONTEXT_FIELD}
    067     * @param cpBands       constant pool bands
    068     * @param segmentHeader segment header
    069     * @param effort        packing effort
    070     */
    071    public MetadataBandGroup(final String type, final int context, final CpBands cpBands, final SegmentHeader segmentHeader, final int effort) {
    072        super(effort, segmentHeader);
    073        this.type = type;
    074        this.cpBands = cpBands;
    075        this.context = context;
    076    }
    077
    078    /**
    079     * Add an annotation to this set of bands
    080     *
    081     * @param desc       TODO
    082     * @param nameRU     TODO
    083     * @param tags       TODO
    084     * @param values     TODO
    085     * @param caseArrayN TODO
    086     * @param nestTypeRS TODO
    087     * @param nestNameRU TODO
    088     * @param nestPairN  TODO
    089     */
    090    public void addAnnotation(final String desc, final List<String> nameRU, final List<String> tags, final List<Object> values, final List<Integer> caseArrayN,
    091            final List<String> nestTypeRS, final List<String> nestNameRU, final List<Integer> nestPairN) {
    092        type_RS.add(cpBands.getCPSignature(desc));
    093        pair_N.add(nameRU.size());
    094        nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name)));
    095
    096        final Iterator<Object> valuesIterator = values.iterator();
    097        for (final String tag : tags) {
    098            T.add(tag);
    099            switch (tag) {
    100            case "B":
    101            case "C":
    102            case "I":
    103            case "S":
    104            case "Z": {
    105                caseI_KI.add(cpBands.getConstant(valuesIterator.next()));
    106                break;
    107            }
    108            case "D": {
    109                caseD_KD.add(cpBands.getConstant(valuesIterator.next()));
    110                break;
    111            }
    112            case "F": {
    113                caseF_KF.add(cpBands.getConstant(valuesIterator.next()));
    114                break;
    115            }
    116            case "J": {
    117                caseJ_KJ.add(cpBands.getConstant(valuesIterator.next()));
    118                break;
    119            }
    120            case "c": {
    121                casec_RS.add(cpBands.getCPSignature(nextString(valuesIterator)));
    122                break;
    123            }
    124            case "e": {
    125                caseet_RS.add(cpBands.getCPSignature(nextString(valuesIterator)));
    126                caseec_RU.add(cpBands.getCPUtf8(nextString(valuesIterator)));
    127                break;
    128            }
    129            case "s": {
    130                cases_RU.add(cpBands.getCPUtf8(nextString(valuesIterator)));
    131                break;
    132            }
    133            }
    134            // do nothing here for [ or @ (handled below)
    135        }
    136        for (final Integer element : caseArrayN) {
    137            final int arraySize = element.intValue();
    138            casearray_N.add(arraySize);
    139            numBackwardsCalls += arraySize;
    140        }
    141        nestTypeRS.forEach(element -> nesttype_RS.add(cpBands.getCPSignature(element)));
    142        nestNameRU.forEach(element -> nestname_RU.add(cpBands.getCPUtf8(element)));
    143        for (final Integer numPairs : nestPairN) {
    144            nestpair_N.add(numPairs.intValue());
    145            numBackwardsCalls += numPairs.intValue();
    146        }
    147    }
    148
    149    /**
    150     * Add an annotation to this set of bands.
    151     *
    152     * @param numParams  TODO
    153     * @param annoN      TODO
    154     * @param pairN      TODO
    155     * @param typeRS     TODO
    156     * @param nameRU     TODO
    157     * @param tags       TODO
    158     * @param values     TODO
    159     * @param caseArrayN TODO
    160     * @param nestTypeRS TODO
    161     * @param nestNameRU TODO
    162     * @param nestPairN  TODO
    163     */
    164    public void addParameterAnnotation(final int numParams, final int[] annoN, final IntList pairN, final List<String> typeRS, final List<String> nameRU,
    165            final List<String> tags, final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS, final List<String> nestNameRU,
    166            final List<Integer> nestPairN) {
    167        param_NB.add(numParams);
    168        for (final int element : annoN) {
    169            anno_N.add(element);
    170        }
    171        pair_N.addAll(pairN);
    172        typeRS.forEach(desc -> type_RS.add(cpBands.getCPSignature(desc)));
    173        nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name)));
    174        final Iterator<Object> valuesIterator = values.iterator();
    175        for (final String tag : tags) {
    176            T.add(tag);
    177            switch (tag) {
    178            case "B":
    179            case "C":
    180            case "I":
    181            case "S":
    182            case "Z": {
    183                caseI_KI.add(cpBands.getConstant(valuesIterator.next()));
    184                break;
    185            }
    186            case "D": {
    187                caseD_KD.add(cpBands.getConstant(valuesIterator.next()));
    188                break;
    189            }
    190            case "F": {
    191                caseF_KF.add(cpBands.getConstant(valuesIterator.next()));
    192                break;
    193            }
    194            case "J": {
    195                caseJ_KJ.add(cpBands.getConstant(valuesIterator.next()));
    196                break;
    197            }
    198            case "c": {
    199                casec_RS.add(cpBands.getCPSignature(nextString(valuesIterator)));
    200                break;
    201            }
    202            case "e": {
    203                caseet_RS.add(cpBands.getCPSignature(nextString(valuesIterator)));
    204                caseec_RU.add(cpBands.getCPUtf8(nextString(valuesIterator)));
    205                break;
    206            }
    207            case "s": {
    208                cases_RU.add(cpBands.getCPUtf8(nextString(valuesIterator)));
    209                break;
    210            }
    211            }
    212            // do nothing here for [ or @ (handled below)
    213        }
    214        for (final Integer element : caseArrayN) {
    215            final int arraySize = element.intValue();
    216            casearray_N.add(arraySize);
    217            numBackwardsCalls += arraySize;
    218        }
    219        nestTypeRS.forEach(type -> nesttype_RS.add(cpBands.getCPSignature(type)));
    220        nestNameRU.forEach(name -> nestname_RU.add(cpBands.getCPUtf8(name)));
    221        for (final Integer numPairs : nestPairN) {
    222            nestpair_N.add(numPairs.intValue());
    223            numBackwardsCalls += numPairs.intValue();
    224        }
    225    }
    226
    227    /**
    228     * Returns true if any annotations have been added to this set of bands.
    229     *
    230     * @return true if any annotations have been added to this set of bands.
    231     */
    232    public boolean hasContent() {
    233        return type_RS.size() > 0;
    234    }
    235
    236    public void incrementAnnoN() {
    237        anno_N.increment(anno_N.size() - 1);
    238    }
    239
    240    public void newEntryInAnnoN() {
    241        anno_N.add(1);
    242    }
    243
    244    private String nextString(final Iterator<Object> valuesIterator) {
    245        return (String) valuesIterator.next();
    246    }
    247
    248    public int numBackwardsCalls() {
    249        return numBackwardsCalls;
    250    }
    251
    252    /*
    253     * (non-Javadoc)
    254     *
    255     * @see org.apache.commons.compress.harmony.pack200.BandSet#pack(java.io.OutputStream)
    256     */
    257    @Override
    258    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    259        PackingUtils.log("Writing metadata band group...");
    260        if (hasContent()) {
    261            final String contextStr;
    262            if (context == CONTEXT_CLASS) {
    263                contextStr = "Class";
    264            } else if (context == CONTEXT_FIELD) {
    265                contextStr = "Field";
    266            } else {
    267                contextStr = "Method";
    268            }
    269            byte[] encodedBand;
    270            if (!type.equals("AD")) {
    271                if (type.indexOf('P') != -1) {
    272                    // Parameter annotation so we need to transmit param_NB
    273                    encodedBand = encodeBandInt(contextStr + "_" + type + " param_NB", param_NB.toArray(), Codec.BYTE1);
    274                    out.write(encodedBand);
    275                    PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " anno_N[" + param_NB.size() + "]");
    276                }
    277                encodedBand = encodeBandInt(contextStr + "_" + type + " anno_N", anno_N.toArray(), Codec.UNSIGNED5);
    278                out.write(encodedBand);
    279                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " anno_N[" + anno_N.size() + "]");
    280
    281                encodedBand = encodeBandInt(contextStr + "_" + type + " type_RS", cpEntryListToArray(type_RS), Codec.UNSIGNED5);
    282                out.write(encodedBand);
    283                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " type_RS[" + type_RS.size() + "]");
    284
    285                encodedBand = encodeBandInt(contextStr + "_" + type + " pair_N", pair_N.toArray(), Codec.UNSIGNED5);
    286                out.write(encodedBand);
    287                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " pair_N[" + pair_N.size() + "]");
    288
    289                encodedBand = encodeBandInt(contextStr + "_" + type + " name_RU", cpEntryListToArray(name_RU), Codec.UNSIGNED5);
    290                out.write(encodedBand);
    291                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " name_RU[" + name_RU.size() + "]");
    292            }
    293            encodedBand = encodeBandInt(contextStr + "_" + type + " T", tagListToArray(T), Codec.BYTE1);
    294            out.write(encodedBand);
    295            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " T[" + T.size() + "]");
    296
    297            encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI", cpEntryListToArray(caseI_KI), Codec.UNSIGNED5);
    298            out.write(encodedBand);
    299            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseI_KI[" + caseI_KI.size() + "]");
    300
    301            encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD", cpEntryListToArray(caseD_KD), Codec.UNSIGNED5);
    302            out.write(encodedBand);
    303            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseD_KD[" + caseD_KD.size() + "]");
    304
    305            encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF", cpEntryListToArray(caseF_KF), Codec.UNSIGNED5);
    306            out.write(encodedBand);
    307            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseF_KF[" + caseF_KF.size() + "]");
    308
    309            encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ", cpEntryListToArray(caseJ_KJ), Codec.UNSIGNED5);
    310            out.write(encodedBand);
    311            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseJ_KJ[" + caseJ_KJ.size() + "]");
    312
    313            encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS", cpEntryListToArray(casec_RS), Codec.UNSIGNED5);
    314            out.write(encodedBand);
    315            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casec_RS[" + casec_RS.size() + "]");
    316
    317            encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS", cpEntryListToArray(caseet_RS), Codec.UNSIGNED5);
    318            out.write(encodedBand);
    319            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseet_RS[" + caseet_RS.size() + "]");
    320
    321            encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU", cpEntryListToArray(caseec_RU), Codec.UNSIGNED5);
    322            out.write(encodedBand);
    323            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseec_RU[" + caseec_RU.size() + "]");
    324
    325            encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU", cpEntryListToArray(cases_RU), Codec.UNSIGNED5);
    326            out.write(encodedBand);
    327            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " cases_RU[" + cases_RU.size() + "]");
    328
    329            encodedBand = encodeBandInt(contextStr + "_" + type + " casearray_N", casearray_N.toArray(), Codec.UNSIGNED5);
    330            out.write(encodedBand);
    331            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casearray_N[" + casearray_N.size() + "]");
    332
    333            encodedBand = encodeBandInt(contextStr + "_" + type + " nesttype_RS", cpEntryListToArray(nesttype_RS), Codec.UNSIGNED5);
    334            out.write(encodedBand);
    335            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nesttype_RS[" + nesttype_RS.size() + "]");
    336
    337            encodedBand = encodeBandInt(contextStr + "_" + type + " nestpair_N", nestpair_N.toArray(), Codec.UNSIGNED5);
    338            out.write(encodedBand);
    339            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestpair_N[" + nestpair_N.size() + "]");
    340
    341            encodedBand = encodeBandInt(contextStr + "_" + type + " nestname_RU", cpEntryListToArray(nestname_RU), Codec.UNSIGNED5);
    342            out.write(encodedBand);
    343            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestname_RU[" + nestname_RU.size() + "]");
    344        }
    345    }
    346
    347    /**
    348     * Remove the latest annotation that was added to this group
    349     */
    350    public void removeLatest() {
    351        final int latest = anno_N.remove(anno_N.size() - 1);
    352        for (int i = 0; i < latest; i++) {
    353            type_RS.remove(type_RS.size() - 1);
    354            final int pairs = pair_N.remove(pair_N.size() - 1);
    355            for (int j = 0; j < pairs; j++) {
    356                removeOnePair();
    357            }
    358        }
    359    }
    360
    361    /*
    362     * Convenience method for removeLatest
    363     */
    364    private void removeOnePair() {
    365        final String tag = T.remove(T.size() - 1);
    366        switch (tag) {
    367        case "B":
    368        case "C":
    369        case "I":
    370        case "S":
    371        case "Z":
    372            caseI_KI.remove(caseI_KI.size() - 1);
    373            break;
    374        case "D":
    375            caseD_KD.remove(caseD_KD.size() - 1);
    376            break;
    377        case "F":
    378            caseF_KF.remove(caseF_KF.size() - 1);
    379            break;
    380        case "J":
    381            caseJ_KJ.remove(caseJ_KJ.size() - 1);
    382            break;
    383        case "e":
    384            caseet_RS.remove(caseet_RS.size() - 1);
    385            caseec_RU.remove(caseet_RS.size() - 1);
    386            break;
    387        case "s":
    388            cases_RU.remove(cases_RU.size() - 1);
    389            break;
    390        case "[":
    391            final int arraySize = casearray_N.remove(casearray_N.size() - 1);
    392            numBackwardsCalls -= arraySize;
    393            for (int k = 0; k < arraySize; k++) {
    394                removeOnePair();
    395            }
    396            break;
    397        case "@":
    398            nesttype_RS.remove(nesttype_RS.size() - 1);
    399            final int numPairs = nestpair_N.remove(nestpair_N.size() - 1);
    400            numBackwardsCalls -= numPairs;
    401            for (int i = 0; i < numPairs; i++) {
    402                removeOnePair();
    403            }
    404            break;
    405        }
    406    }
    407
    408    private int[] tagListToArray(final List<String> list) {
    409        return list.stream().mapToInt(s -> s.charAt(0)).toArray();
    410    }
    411
    412}