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}