    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,
    069        final SegmentHeader segmentHeader, final int effort) {
    070        super(effort, segmentHeader);
    071        this.type = type;
    072        this.cpBands = cpBands;
    073        this.context = context;
    074    }
    075
    076    /**
    077     * Add an annotation to this set of bands
    078     *
    079     * @param desc TODO
    080     * @param nameRU TODO
    081     * @param tags TODO
    082     * @param values TODO
    083     * @param caseArrayN TODO
    084     * @param nestTypeRS TODO
    085     * @param nestNameRU TODO
    086     * @param nestPairN TODO
    087     */
    088        public void addAnnotation(final String desc, final List<String> nameRU, final List<String> tags,
    089                        final List<Object> values, final List<Integer> caseArrayN, final List<String> nestTypeRS,
    090                        final List<String> nestNameRU, final List<Integer> nestPairN) {
    091                type_RS.add(cpBands.getCPSignature(desc));
    092                pair_N.add(nameRU.size());
    093                nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name)));
    094
    095                final Iterator<Object> valuesIterator = values.iterator();
    096                for (final String tag : tags) {
    097                        T.add(tag);
    098            switch (tag) {
    099                case "B":
    100                case "C":
    101                case "I":
    102                case "S":
    103                case "Z": {
    104                    caseI_KI.add(cpBands.getConstant(valuesIterator.next()));
    105                    break;
    106                }
    107                case "D": {
    108                    caseD_KD.add(cpBands.getConstant(valuesIterator.next()));
    109                    break;
    110                }
    111                case "F": {
    112                    caseF_KF.add(cpBands.getConstant(valuesIterator.next()));
    113                    break;
    114                }
    115                case "J": {
    116                    caseJ_KJ.add(cpBands.getConstant(valuesIterator.next()));
    117                    break;
    118                }
    119                case "c": {
    120                    casec_RS.add(cpBands.getCPSignature(nextString(valuesIterator)));
    121                    break;
    122                }
    123                case "e": {
    124                    caseet_RS.add(cpBands.getCPSignature(nextString(valuesIterator)));
    125                    caseec_RU.add(cpBands.getCPUtf8(nextString(valuesIterator)));
    126                    break;
    127                }
    128                case "s": {
    129                    cases_RU.add(cpBands.getCPUtf8(nextString(valuesIterator)));
    130                    break;
    131                }
    132            }
    133                        // do nothing here for [ or @ (handled below)
    134                }
    135                for (final Integer element : caseArrayN) {
    136                        final int arraySize = element.intValue();
    137                        casearray_N.add(arraySize);
    138                        numBackwardsCalls += arraySize;
    139                }
    140                nestTypeRS.forEach(element -> nesttype_RS.add(cpBands.getCPSignature(element)));
    141                nestNameRU.forEach(element -> nestname_RU.add(cpBands.getCPUtf8(element)));
    142                for (final Integer numPairs : nestPairN) {
    143                        nestpair_N.add(numPairs.intValue());
    144                        numBackwardsCalls += numPairs.intValue();
    145                }
    146        }
    147
    148    /**
    149     * Add an annotation to this set of bands.
    150     *
    151     * @param numParams TODO
    152     * @param annoN TODO
    153     * @param pairN TODO
    154     * @param typeRS TODO
    155     * @param nameRU TODO
    156     * @param tags TODO
    157     * @param values TODO
    158     * @param caseArrayN TODO
    159     * @param nestTypeRS TODO
    160     * @param nestNameRU TODO
    161     * @param nestPairN TODO
    162     */
    163        public void addParameterAnnotation(final int numParams, final int[] annoN, final IntList pairN,
    164                        final List<String> typeRS, final List<String> nameRU, final List<String> tags, final List<Object> values,
    165                        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            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
    276                        + " anno_N[" + param_NB.size() + "]");
    277                }
    278                encodedBand = encodeBandInt(contextStr + "_" + type + " anno_N", anno_N.toArray(), Codec.UNSIGNED5);
    279                out.write(encodedBand);
    280                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " anno_N["
    281                    + anno_N.size() + "]");
    282
    283                encodedBand = encodeBandInt(contextStr + "_" + type + " type_RS", cpEntryListToArray(type_RS),
    284                    Codec.UNSIGNED5);
    285                out.write(encodedBand);
    286                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " type_RS["
    287                    + type_RS.size() + "]");
    288
    289                encodedBand = encodeBandInt(contextStr + "_" + type + " pair_N", pair_N.toArray(), Codec.UNSIGNED5);
    290                out.write(encodedBand);
    291                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " pair_N["
    292                    + pair_N.size() + "]");
    293
    294                encodedBand = encodeBandInt(contextStr + "_" + type + " name_RU", cpEntryListToArray(name_RU),
    295                    Codec.UNSIGNED5);
    296                out.write(encodedBand);
    297                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " name_RU["
    298                    + name_RU.size() + "]");
    299            }
    300            encodedBand = encodeBandInt(contextStr + "_" + type + " T", tagListToArray(T), Codec.BYTE1);
    301            out.write(encodedBand);
    302            PackingUtils
    303                .log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " T[" + T.size() + "]");
    304
    305            encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI", cpEntryListToArray(caseI_KI),
    306                Codec.UNSIGNED5);
    307            out.write(encodedBand);
    308            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseI_KI["
    309                + caseI_KI.size() + "]");
    310
    311            encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD", cpEntryListToArray(caseD_KD),
    312                Codec.UNSIGNED5);
    313            out.write(encodedBand);
    314            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseD_KD["
    315                + caseD_KD.size() + "]");
    316
    317            encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF", cpEntryListToArray(caseF_KF),
    318                Codec.UNSIGNED5);
    319            out.write(encodedBand);
    320            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseF_KF["
    321                + caseF_KF.size() + "]");
    322
    323            encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ", cpEntryListToArray(caseJ_KJ),
    324                Codec.UNSIGNED5);
    325            out.write(encodedBand);
    326            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseJ_KJ["
    327                + caseJ_KJ.size() + "]");
    328
    329            encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS", cpEntryListToArray(casec_RS),
    330                Codec.UNSIGNED5);
    331            out.write(encodedBand);
    332            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casec_RS["
    333                + casec_RS.size() + "]");
    334
    335            encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS", cpEntryListToArray(caseet_RS),
    336                Codec.UNSIGNED5);
    337            out.write(encodedBand);
    338            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseet_RS["
    339                + caseet_RS.size() + "]");
    340
    341            encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU", cpEntryListToArray(caseec_RU),
    342                Codec.UNSIGNED5);
    343            out.write(encodedBand);
    344            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseec_RU["
    345                + caseec_RU.size() + "]");
    346
    347            encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU", cpEntryListToArray(cases_RU),
    348                Codec.UNSIGNED5);
    349            out.write(encodedBand);
    350            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " cases_RU["
    351                + cases_RU.size() + "]");
    352
    353            encodedBand = encodeBandInt(contextStr + "_" + type + " casearray_N", casearray_N.toArray(),
    354                Codec.UNSIGNED5);
    355            out.write(encodedBand);
    356            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casearray_N["
    357                + casearray_N.size() + "]");
    358
    359            encodedBand = encodeBandInt(contextStr + "_" + type + " nesttype_RS", cpEntryListToArray(nesttype_RS),
    360                Codec.UNSIGNED5);
    361            out.write(encodedBand);
    362            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nesttype_RS["
    363                + nesttype_RS.size() + "]");
    364
    365            encodedBand = encodeBandInt(contextStr + "_" + type + " nestpair_N", nestpair_N.toArray(), Codec.UNSIGNED5);
    366            out.write(encodedBand);
    367            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestpair_N["
    368                + nestpair_N.size() + "]");
    369
    370            encodedBand = encodeBandInt(contextStr + "_" + type + " nestname_RU", cpEntryListToArray(nestname_RU),
    371                Codec.UNSIGNED5);
    372            out.write(encodedBand);
    373            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestname_RU["
    374                + nestname_RU.size() + "]");
    375        }
    376    }
    377
    378    /**
    379     * Remove the latest annotation that was added to this group
    380     */
    381    public void removeLatest() {
    382        final int latest = anno_N.remove(anno_N.size() - 1);
    383        for (int i = 0; i < latest; i++) {
    384            type_RS.remove(type_RS.size() - 1);
    385            final int pairs = pair_N.remove(pair_N.size() - 1);
    386            for (int j = 0; j < pairs; j++) {
    387                removeOnePair();
    388            }
    389        }
    390    }
    391
    392    /*
    393     * Convenience method for removeLatest
    394     */
    395    private void removeOnePair() {
    396        final String tag = T.remove(T.size() - 1);
    397        switch (tag) {
    398            case "B":
    399            case "C":
    400            case "I":
    401            case "S":
    402            case "Z":
    403                caseI_KI.remove(caseI_KI.size() - 1);
    404                break;
    405            case "D":
    406                caseD_KD.remove(caseD_KD.size() - 1);
    407                break;
    408            case "F":
    409                caseF_KF.remove(caseF_KF.size() - 1);
    410                break;
    411            case "J":
    412                caseJ_KJ.remove(caseJ_KJ.size() - 1);
    413                break;
    414            case "e":
    415                caseet_RS.remove(caseet_RS.size() - 1);
    416                caseec_RU.remove(caseet_RS.size() - 1);
    417                break;
    418            case "s":
    419                cases_RU.remove(cases_RU.size() - 1);
    420                break;
    421            case "[":
    422                final int arraySize = casearray_N.remove(casearray_N.size() - 1);
    423                numBackwardsCalls -= arraySize;
    424                for (int k = 0; k < arraySize; k++) {
    425                    removeOnePair();
    426                }
    427                break;
    428            case "@":
    429                nesttype_RS.remove(nesttype_RS.size() - 1);
    430                final int numPairs = nestpair_N.remove(nestpair_N.size() - 1);
    431                numBackwardsCalls -= numPairs;
    432                for (int i = 0; i < numPairs; i++) {
    433                    removeOnePair();
    434                }
    435                break;
    436        }
    437    }
    438
    439    private int[] tagListToArray(final List<String> list) {
    440        return list.stream().mapToInt(s -> s.charAt(0)).toArray();
    441    }
    442
    443}