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