View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.compress.harmony.pack200;
21  
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  /**
29   * A group of metadata (annotation) bands, such as class_RVA_bands, method_AD_bands etc.
30   */
31  public class MetadataBandGroup extends BandSet {
32  
33      public static final int CONTEXT_CLASS = 0;
34      public static final int CONTEXT_FIELD = 1;
35      public static final int CONTEXT_METHOD = 2;
36  
37      private final String type;
38      private int numBackwardsCalls;
39  
40      public IntList param_NB = new IntList(); // TODO: Lazy instantiation?
41      public IntList anno_N = new IntList();
42      public List<CPSignature> type_RS = new ArrayList<>();
43      public IntList pair_N = new IntList();
44      public List<CPUTF8> name_RU = new ArrayList<>();
45      public List<String> T = new ArrayList<>();
46      public List<CPConstant<?>> caseI_KI = new ArrayList<>();
47      public List<CPConstant<?>> caseD_KD = new ArrayList<>();
48      public List<CPConstant<?>> caseF_KF = new ArrayList<>();
49      public List<CPConstant<?>> caseJ_KJ = new ArrayList<>();
50      public List<CPSignature> casec_RS = new ArrayList<>();
51      public List<CPSignature> caseet_RS = new ArrayList<>();
52      public List<CPUTF8> caseec_RU = new ArrayList<>();
53      public List<CPUTF8> cases_RU = new ArrayList<>();
54      public IntList casearray_N = new IntList();
55      public List<CPSignature> nesttype_RS = new ArrayList<>();
56      public IntList nestpair_N = new IntList();
57      public List<CPUTF8> nestname_RU = new ArrayList<>();
58  
59      private final CpBands cpBands;
60      private final int context;
61  
62      /**
63       * Constructs a new MetadataBandGroup
64       *
65       * @param type          must be either AD, RVA, RIA, RVPA or RIPA.
66       * @param context       {@code CONTEXT_CLASS}, {@code CONTEXT_METHOD} or {@code CONTEXT_FIELD}
67       * @param cpBands       constant pool bands
68       * @param segmentHeader segment header
69       * @param effort        packing effort
70       */
71      public MetadataBandGroup(final String type, final int context, final CpBands cpBands, final SegmentHeader segmentHeader, final int effort) {
72          super(effort, segmentHeader);
73          this.type = type;
74          this.cpBands = cpBands;
75          this.context = context;
76      }
77  
78      /**
79       * Add an annotation to this set of bands
80       *
81       * @param desc       TODO
82       * @param nameRU     TODO
83       * @param tags       TODO
84       * @param values     TODO
85       * @param caseArrayN TODO
86       * @param nestTypeRS TODO
87       * @param nestNameRU TODO
88       * @param nestPairN  TODO
89       */
90      public void addAnnotation(final String desc, final List<String> nameRU, final List<String> tags, final List<Object> values, final List<Integer> caseArrayN,
91              final List<String> nestTypeRS, final List<String> nestNameRU, final List<Integer> nestPairN) {
92          type_RS.add(cpBands.getCPSignature(desc));
93          pair_N.add(nameRU.size());
94          nameRU.forEach(name -> name_RU.add(cpBands.getCPUtf8(name)));
95  
96          final Iterator<Object> valuesIterator = values.iterator();
97          for (final String tag : tags) {
98              T.add(tag);
99              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 }