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}