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 type_RS = new ArrayList();
040    public IntList pair_N = new IntList();
041    public List name_RU = new ArrayList();
042    public List T = new ArrayList();
043    public List caseI_KI = new ArrayList();
044    public List caseD_KD = new ArrayList();
045    public List caseF_KF = new ArrayList();
046    public List caseJ_KJ = new ArrayList();
047    public List casec_RS = new ArrayList();
048    public List caseet_RS = new ArrayList();
049    public List caseec_RU = new ArrayList();
050    public List cases_RU = new ArrayList();
051    public IntList casearray_N = new IntList();
052    public List nesttype_RS = new ArrayList();
053    public IntList nestpair_N = new IntList();
054    public List 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>, <code>CONTEXT_METHOD</code> or <code>CONTEXT_FIELD</code>
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     * (non-Javadoc)
078     * 
079     * @see org.apache.commons.compress.harmony.pack200.BandSet#pack(java.io.OutputStream)
080     */
081    @Override
082    public void pack(final OutputStream out) throws IOException, Pack200Exception {
083        PackingUtils.log("Writing metadata band group...");
084        if (hasContent()) {
085            String contextStr;
086            if (context == CONTEXT_CLASS) {
087                contextStr = "Class";
088            } else if (context == CONTEXT_FIELD) {
089                contextStr = "Field";
090            } else {
091                contextStr = "Method";
092            }
093            byte[] encodedBand = null;
094            if (!type.equals("AD")) {
095                if (type.indexOf('P') != -1) {
096                    // Parameter annotation so we need to transmit param_NB
097                    encodedBand = encodeBandInt(contextStr + "_" + type + " param_NB", param_NB.toArray(), Codec.BYTE1);
098                    out.write(encodedBand);
099                    PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type
100                        + " anno_N[" + param_NB.size() + "]");
101                }
102                encodedBand = encodeBandInt(contextStr + "_" + type + " anno_N", anno_N.toArray(), Codec.UNSIGNED5);
103                out.write(encodedBand);
104                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " anno_N["
105                    + anno_N.size() + "]");
106
107                encodedBand = encodeBandInt(contextStr + "_" + type + " type_RS", cpEntryListToArray(type_RS),
108                    Codec.UNSIGNED5);
109                out.write(encodedBand);
110                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " type_RS["
111                    + type_RS.size() + "]");
112
113                encodedBand = encodeBandInt(contextStr + "_" + type + " pair_N", pair_N.toArray(), Codec.UNSIGNED5);
114                out.write(encodedBand);
115                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " pair_N["
116                    + pair_N.size() + "]");
117
118                encodedBand = encodeBandInt(contextStr + "_" + type + " name_RU", cpEntryListToArray(name_RU),
119                    Codec.UNSIGNED5);
120                out.write(encodedBand);
121                PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " name_RU["
122                    + name_RU.size() + "]");
123            }
124            encodedBand = encodeBandInt(contextStr + "_" + type + " T", tagListToArray(T), Codec.BYTE1);
125            out.write(encodedBand);
126            PackingUtils
127                .log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " T[" + T.size() + "]");
128
129            encodedBand = encodeBandInt(contextStr + "_" + type + " caseI_KI", cpEntryListToArray(caseI_KI),
130                Codec.UNSIGNED5);
131            out.write(encodedBand);
132            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseI_KI["
133                + caseI_KI.size() + "]");
134
135            encodedBand = encodeBandInt(contextStr + "_" + type + " caseD_KD", cpEntryListToArray(caseD_KD),
136                Codec.UNSIGNED5);
137            out.write(encodedBand);
138            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseD_KD["
139                + caseD_KD.size() + "]");
140
141            encodedBand = encodeBandInt(contextStr + "_" + type + " caseF_KF", cpEntryListToArray(caseF_KF),
142                Codec.UNSIGNED5);
143            out.write(encodedBand);
144            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseF_KF["
145                + caseF_KF.size() + "]");
146
147            encodedBand = encodeBandInt(contextStr + "_" + type + " caseJ_KJ", cpEntryListToArray(caseJ_KJ),
148                Codec.UNSIGNED5);
149            out.write(encodedBand);
150            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseJ_KJ["
151                + caseJ_KJ.size() + "]");
152
153            encodedBand = encodeBandInt(contextStr + "_" + type + " casec_RS", cpEntryListToArray(casec_RS),
154                Codec.UNSIGNED5);
155            out.write(encodedBand);
156            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casec_RS["
157                + casec_RS.size() + "]");
158
159            encodedBand = encodeBandInt(contextStr + "_" + type + " caseet_RS", cpEntryListToArray(caseet_RS),
160                Codec.UNSIGNED5);
161            out.write(encodedBand);
162            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseet_RS["
163                + caseet_RS.size() + "]");
164
165            encodedBand = encodeBandInt(contextStr + "_" + type + " caseec_RU", cpEntryListToArray(caseec_RU),
166                Codec.UNSIGNED5);
167            out.write(encodedBand);
168            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " caseec_RU["
169                + caseec_RU.size() + "]");
170
171            encodedBand = encodeBandInt(contextStr + "_" + type + " cases_RU", cpEntryListToArray(cases_RU),
172                Codec.UNSIGNED5);
173            out.write(encodedBand);
174            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " cases_RU["
175                + cases_RU.size() + "]");
176
177            encodedBand = encodeBandInt(contextStr + "_" + type + " casearray_N", casearray_N.toArray(),
178                Codec.UNSIGNED5);
179            out.write(encodedBand);
180            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " casearray_N["
181                + casearray_N.size() + "]");
182
183            encodedBand = encodeBandInt(contextStr + "_" + type + " nesttype_RS", cpEntryListToArray(nesttype_RS),
184                Codec.UNSIGNED5);
185            out.write(encodedBand);
186            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nesttype_RS["
187                + nesttype_RS.size() + "]");
188
189            encodedBand = encodeBandInt(contextStr + "_" + type + " nestpair_N", nestpair_N.toArray(), Codec.UNSIGNED5);
190            out.write(encodedBand);
191            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestpair_N["
192                + nestpair_N.size() + "]");
193
194            encodedBand = encodeBandInt(contextStr + "_" + type + " nestname_RU", cpEntryListToArray(nestname_RU),
195                Codec.UNSIGNED5);
196            out.write(encodedBand);
197            PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + contextStr + "_" + type + " nestname_RU["
198                + nestname_RU.size() + "]");
199        }
200    }
201
202    private int[] tagListToArray(final List t2) {
203        final int[] ints = new int[t2.size()];
204        for (int i = 0; i < ints.length; i++) {
205            ints[i] = ((String) t2.get(i)).charAt(0);
206        }
207        return ints;
208    }
209
210    /**
211     * Add an annotation to this set of bands.
212     * 
213     * @param numParams TODO
214     * @param annoN TODO
215     * @param pairN TODO
216     * @param typeRS TODO
217     * @param nameRU TODO
218     * @param t TODO
219     * @param values TODO
220     * @param caseArrayN TODO
221     * @param nestTypeRS TODO
222     * @param nestNameRU TODO
223     * @param nestPairN TODO
224     */
225    public void addParameterAnnotation(final int numParams, final int[] annoN, final IntList pairN, final List typeRS,
226        final List nameRU, final List t, final List values, final List caseArrayN, final List nestTypeRS,
227        final List nestNameRU, final List nestPairN) {
228        param_NB.add(numParams);
229        for (int i = 0; i < annoN.length; i++) {
230            anno_N.add(annoN[i]);
231        }
232        pair_N.addAll(pairN);
233        for (final Iterator iterator = typeRS.iterator(); iterator.hasNext();) {
234            final String desc = (String) iterator.next();
235            type_RS.add(cpBands.getCPSignature(desc));
236        }
237        for (final Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
238            final String name = (String) iterator.next();
239            name_RU.add(cpBands.getCPUtf8(name));
240        }
241        final Iterator valuesIterator = values.iterator();
242        for (final Iterator iterator = t.iterator(); iterator.hasNext();) {
243            final String tag = (String) iterator.next();
244            T.add(tag);
245            if (tag.equals("B") || tag.equals("C") || tag.equals("I") || tag.equals("S") || tag.equals("Z")) {
246                final Integer value = (Integer) valuesIterator.next();
247                caseI_KI.add(cpBands.getConstant(value));
248            } else if (tag.equals("D")) {
249                final Double value = (Double) valuesIterator.next();
250                caseD_KD.add(cpBands.getConstant(value));
251            } else if (tag.equals("F")) {
252                final Float value = (Float) valuesIterator.next();
253                caseF_KF.add(cpBands.getConstant(value));
254            } else if (tag.equals("J")) {
255                final Long value = (Long) valuesIterator.next();
256                caseJ_KJ.add(cpBands.getConstant(value));
257            } else if (tag.equals("c")) {
258                final String value = (String) valuesIterator.next();
259                casec_RS.add(cpBands.getCPSignature(value));
260            } else if (tag.equals("e")) {
261                final String value = (String) valuesIterator.next();
262                final String value2 = (String) valuesIterator.next();
263                caseet_RS.add(cpBands.getCPSignature(value));
264                caseec_RU.add(cpBands.getCPUtf8(value2));
265            } else if (tag.equals("s")) {
266                final String value = (String) valuesIterator.next();
267                cases_RU.add(cpBands.getCPUtf8(value));
268            }
269            // do nothing here for [ or @ (handled below)
270        }
271        for (final Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
272            final int arraySize = ((Integer) iterator.next()).intValue();
273            casearray_N.add(arraySize);
274            numBackwardsCalls += arraySize;
275        }
276        for (final Iterator iterator = nestTypeRS.iterator(); iterator.hasNext();) {
277            final String type = (String) iterator.next();
278            nesttype_RS.add(cpBands.getCPSignature(type));
279        }
280        for (final Iterator iterator = nestNameRU.iterator(); iterator.hasNext();) {
281            final String name = (String) iterator.next();
282            nestname_RU.add(cpBands.getCPUtf8(name));
283        }
284        for (final Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
285            final Integer numPairs = (Integer) iterator.next();
286            nestpair_N.add(numPairs.intValue());
287            numBackwardsCalls += numPairs.intValue();
288        }
289    }
290
291    /**
292     * Add an annotation to this set of bands
293     *
294     * @param desc TODO
295     * @param nameRU TODO
296     * @param t TODO
297     * @param values TODO
298     * @param caseArrayN TODO
299     * @param nestTypeRS TODO
300     * @param nestNameRU TODO
301     * @param nestPairN TODO
302     */
303    public void addAnnotation(final String desc, final List nameRU, final List t, final List values,
304        final List caseArrayN, final List nestTypeRS, final List nestNameRU, final List nestPairN) {
305        type_RS.add(cpBands.getCPSignature(desc));
306        pair_N.add(nameRU.size());
307
308        for (final Iterator iterator = nameRU.iterator(); iterator.hasNext();) {
309            final String name = (String) iterator.next();
310            name_RU.add(cpBands.getCPUtf8(name));
311        }
312
313        final Iterator valuesIterator = values.iterator();
314        for (final Iterator iterator = t.iterator(); iterator.hasNext();) {
315            final String tag = (String) iterator.next();
316            T.add(tag);
317            if (tag.equals("B") || tag.equals("C") || tag.equals("I") || tag.equals("S") || tag.equals("Z")) {
318                final Integer value = (Integer) valuesIterator.next();
319                caseI_KI.add(cpBands.getConstant(value));
320            } else if (tag.equals("D")) {
321                final Double value = (Double) valuesIterator.next();
322                caseD_KD.add(cpBands.getConstant(value));
323            } else if (tag.equals("F")) {
324                final Float value = (Float) valuesIterator.next();
325                caseF_KF.add(cpBands.getConstant(value));
326            } else if (tag.equals("J")) {
327                final Long value = (Long) valuesIterator.next();
328                caseJ_KJ.add(cpBands.getConstant(value));
329            } else if (tag.equals("c")) {
330                final String value = (String) valuesIterator.next();
331                casec_RS.add(cpBands.getCPSignature(value));
332            } else if (tag.equals("e")) {
333                final String value = (String) valuesIterator.next();
334                final String value2 = (String) valuesIterator.next();
335                caseet_RS.add(cpBands.getCPSignature(value));
336                caseec_RU.add(cpBands.getCPUtf8(value2));
337            } else if (tag.equals("s")) {
338                final String value = (String) valuesIterator.next();
339                cases_RU.add(cpBands.getCPUtf8(value));
340            }
341            // do nothing here for [ or @ (handled below)
342        }
343        for (final Iterator iterator = caseArrayN.iterator(); iterator.hasNext();) {
344            final int arraySize = ((Integer) iterator.next()).intValue();
345            casearray_N.add(arraySize);
346            numBackwardsCalls += arraySize;
347        }
348        for (final Iterator iterator = nestTypeRS.iterator(); iterator.hasNext();) {
349            final String type = (String) iterator.next();
350            nesttype_RS.add(cpBands.getCPSignature(type));
351        }
352        for (final Iterator iterator = nestNameRU.iterator(); iterator.hasNext();) {
353            final String name = (String) iterator.next();
354            nestname_RU.add(cpBands.getCPUtf8(name));
355        }
356        for (final Iterator iterator = nestPairN.iterator(); iterator.hasNext();) {
357            final Integer numPairs = (Integer) iterator.next();
358            nestpair_N.add(numPairs.intValue());
359            numBackwardsCalls += numPairs.intValue();
360        }
361    }
362
363    /**
364     * Returns true if any annotations have been added to this set of bands.
365     *
366     * @return true if any annotations have been added to this set of bands.
367     */
368    public boolean hasContent() {
369        return type_RS.size() > 0;
370    }
371
372    public int numBackwardsCalls() {
373        return numBackwardsCalls;
374    }
375
376    public void incrementAnnoN() {
377        anno_N.increment(anno_N.size() - 1);
378    }
379
380    public void newEntryInAnnoN() {
381        anno_N.add(1);
382    }
383
384    /**
385     * Remove the latest annotation that was added to this group
386     */
387    public void removeLatest() {
388        final int latest = anno_N.remove(anno_N.size() - 1);
389        for (int i = 0; i < latest; i++) {
390            type_RS.remove(type_RS.size() - 1);
391            final int pairs = pair_N.remove(pair_N.size() - 1);
392            for (int j = 0; j < pairs; j++) {
393                removeOnePair();
394            }
395        }
396    }
397
398    /*
399     * Convenience method for removeLatest
400     */
401    private void removeOnePair() {
402        final String tag = (String) T.remove(T.size() - 1);
403        if (tag.equals("B") || tag.equals("C") || tag.equals("I") || tag.equals("S") || tag.equals("Z")) {
404            caseI_KI.remove(caseI_KI.size() - 1);
405        } else if (tag.equals("D")) {
406            caseD_KD.remove(caseD_KD.size() - 1);
407        } else if (tag.equals("F")) {
408            caseF_KF.remove(caseF_KF.size() - 1);
409        } else if (tag.equals("J")) {
410            caseJ_KJ.remove(caseJ_KJ.size() - 1);
411        } else if (tag.equals("C")) {
412            casec_RS.remove(casec_RS.size() - 1);
413        } else if (tag.equals("e")) {
414            caseet_RS.remove(caseet_RS.size() - 1);
415            caseec_RU.remove(caseet_RS.size() - 1);
416        } else if (tag.equals("s")) {
417            cases_RU.remove(cases_RU.size() - 1);
418        } else if (tag.equals("[")) {
419            final int arraySize = casearray_N.remove(casearray_N.size() - 1);
420            numBackwardsCalls -= arraySize;
421            for (int k = 0; k < arraySize; k++) {
422                removeOnePair();
423            }
424        } else if (tag.equals("@")) {
425            nesttype_RS.remove(nesttype_RS.size() - 1);
426            final int numPairs = nestpair_N.remove(nestpair_N.size() - 1);
427            numBackwardsCalls -= numPairs;
428            for (int i = 0; i < numPairs; i++) {
429                removeOnePair();
430            }
431        }
432    }
433
434}