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 = 0;
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}